<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  
  <title>Hexo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta property="og:type" content="website">
<meta property="og:title" content="Hexo">
<meta property="og:url" content="https://goofyer.gitee.io/notes-on-computer-expertise/page/6/index.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="John Doe">
<meta name="twitter:card" content="summary">
  
    <link rel="alternate" href="/notes-on-computer-expertise/atom.xml" title="Hexo" type="application/atom+xml">
  
  
    <link rel="shortcut icon" href="/notes-on-computer-expertise/favicon.png">
  
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typeface-source-code-pro@0.0.71/index.min.css">

  
  
<link rel="stylesheet" href="/notes-on-computer-expertise/css/style.css">

  
    
<link rel="stylesheet" href="/notes-on-computer-expertise/fancybox/jquery.fancybox.min.css">

  
<meta name="generator" content="Hexo 5.4.0"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/notes-on-computer-expertise/" id="logo">Hexo</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/notes-on-computer-expertise/">Home</a>
        
          <a class="main-nav-link" href="/notes-on-computer-expertise/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/notes-on-computer-expertise/atom.xml" title="RSS Feed"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="Search"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="https://goofyer.gitee.io/notes-on-computer-expertise"></form>
      </div>
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main">
  
    <article id="post-神经网络/深度学习基本概念" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/01/02/%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5/" class="article-date">
  <time class="dt-published" datetime="2022-01-02T15:39:46.000Z" itemprop="datePublished">2022-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/notes-on-computer-expertise/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/">深度学习</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/notes-on-computer-expertise/2022/01/02/%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5/">深度学习基本概念</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="课程名称∶深度学习基本概念"><a href="#课程名称∶深度学习基本概念" class="headerlink" title="课程名称∶深度学习基本概念"></a>课程名称∶深度学习基本概念</h1><h2 id="一、实验介绍"><a href="#一、实验介绍" class="headerlink" title="一、实验介绍"></a>一、实验介绍</h2><h3 id="1-1实验内容"><a href="#1-1实验内容" class="headerlink" title="1.1实验内容"></a>1.1实验内容</h3><ul>
<li>深度学习并没有你想象的那么难，本课程将会一边讲解深度学习中的基本理论，一边通过动手使用python实现一个简单的深度神经网络去验证这些理论，让你从原理上真正入门深度学习。</li>
<li>本次实验将会带大家学习深度学习中的一些最基本的概念，本次实验很重要，理解这些概念是继续深入学习的基础。<h3 id="1-2实验知识点"><a href="#1-2实验知识点" class="headerlink" title="1.2实验知识点"></a>1.2实验知识点</h3></li>
<li>如何让机器“学习”</li>
<li>神经网络的概念</li>
<li>有监督与无监督学习的区别</li>
<li>回归与分类的区别</li>
<li>损失函数的概念</li>
<li>梯度下降算法介绍</li>
<li>超参数的概念<h3 id="1-3实验要求"><a href="#1-3实验要求" class="headerlink" title="1.3实验要求"></a>1.3实验要求</h3></li>
<li>本实验要求你在实验前具备以下基本能力∶<ul>
<li>1.熟练使用python</li>
<li>2.具备高中毕业生的数学水平</li>
<li>3.对深度学习的兴趣<h2 id="二、实验步骤"><a href="#二、实验步骤" class="headerlink" title="二、实验步骤"></a>二、实验步骤</h2><h3 id="2-1什么是让机器“学习”"><a href="#2-1什么是让机器“学习”" class="headerlink" title="2.1什么是让机器“学习”"></a>2.1什么是让机器“学习”</h3></li>
</ul>
</li>
<li>如果你只是听说过机器学习或者深度学习，你可能会被他们的名字吓到，机器真的已经能像人一样“学习”了吗?其实，这里的“学习”和人的学习的确有一些相似的地方，但又有很大不同。为了理解这里的“学习”究竟是什么含义，让我们先来回想一下以前我们是如何让机器工作的，我们会以编程计算圆的面积为例。<h4 id="2-1-1计算圆的面积—手工编程的方法"><a href="#2-1-1计算圆的面积—手工编程的方法" class="headerlink" title="2.1.1计算圆的面积—手工编程的方法"></a>2.1.1计算圆的面积—手工编程的方法</h4></li>
<li><p>计算圆的面积显然是一个很简单的问题，任何程序员应该都能立马写出类似以下的函数来进行计算:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#计真圆面积的方法</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">calArea</span>(<span class="params">radius</span>):</span></span><br><span class="line">    area=<span class="number">3.14</span>*radius*radius</span><br><span class="line">    <span class="keyword">return</span> area</span><br></pre></td></tr></table></figure>
</li>
<li><p>在编写这段代码的过程中，其实我们做的事情很简单。因为我们事先知道输入和输出之间的关系(输出等于3.14乘以输入的平方,高中毕业生应该都能轻易得出这个结论吧），所以我们直接将这种关系通过area=3.14XradiusXradisu这个式子表达出来就行了。注意我们设置了一个参数︰圆周率=3.14。</p>
</li>
</ul>
<h4 id="2-1-2计算圆的面积—-让机器“学习”"><a href="#2-1-2计算圆的面积—-让机器“学习”" class="headerlink" title="2.1.2计算圆的面积— 让机器“学习”"></a>2.1.2计算圆的面积— 让机器“学习”</h4><ul>
<li><p>现在让你看看如何让机器通过“学习”来计算圆的面积∶</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">learn</span>(<span class="params">model,data</span>):</span></span><br><span class="line">...<span class="comment">#通过数据data训练模型model的代码</span></span><br><span class="line"><span class="keyword">return</span> model <span class="comment">#已经训练好参数的模型</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">test</span>(<span class="params">model,x</span>):</span></span><br><span class="line">...<span class="comment">#将x输入模型model中得出结果y的代码return y #返回计算结果--圆的面积</span></span><br><span class="line">trained_model=learn(init_model,data)<span class="comment">#通过训练得到模型trained_modely=test(trained_model,x=100)#得到半径为10a的圆的面积</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>其实让机器“学习”很简单不是吗（至少上面的代码看上去是如此^^)，我们提供给机器足够的训练数据data ,然后learn 函数(我们暂时不知道如何实现这个函数，之后的实验我们会逐步实现这个函数）使用这些数据去训练模型model(我们接下来会介绍如何构建这个model)，当我们需要计算一个新的圆的面积时，就调用test函数，根据训练好的模型去计算其面积。注意这里我们不需要事先知道输入和输出之间的关系，也不需要手动设置任何参数，只要把数据“喂给”学习算法learn，它就会自动得出一个能够解决问题的模型。也就是说，在训练过程中，模型<br>model其实是“学习”到了输入和输出之前的关系以及相关的参数。就像是一个数学家通过对圆的观察，得出了圆的面积和半径的关系，并且求出了圆周率。</p>
<h4 id="2-1-3机器学习—激动人心的未来"><a href="#2-1-3机器学习—激动人心的未来" class="headerlink" title="2.1.3机器学习—激动人心的未来"></a>2.1.3机器学习—激动人心的未来</h4><p>这太令人激动了不是吗，如果我们能找到一个完美的学习算法 learn和合适的模型 model，并且提供给它足够的学习数据data，那这个世界上几乎所有问题都可以用这个学习算法来解决。遗憾的是，目前我们的学习算法、学习模型还不够完美，我们的学习数据仍然不够，能够通过机器学习(一般认为深度学习包含在机器学习当中）解决的问题还很有限。不过目前取得的很多成果已经非常激动人心了，比如让人类“重新认识围棋”的AlphaGO、准确率已经接近甚至达到人类水准的语音识别和机器翻译、已经能够上路的自动驾驶汽车。总之，机器学习将会彻底改变机器为我们服务的方式，我们将越来越少的通过编程明确地告诉计算机如何去解决一个问题，而是让计算机自己学习如何解决问题。</p>
<h4 id="2-1-4让机器“学习”的必要性-amp-实验项目的引入"><a href="#2-1-4让机器“学习”的必要性-amp-实验项目的引入" class="headerlink" title="2.1.4让机器“学习”的必要性&amp;实验项目的引入"></a>2.1.4让机器“学习”的必要性&amp;实验项目的引入</h4></li>
<li>上面的例子太过简单，无法体现出让机器学习的必要性，要计算圆的面积,更快的方法显然是直接手工编写代码。但是现实世界中的很多问题是手工编写代码几乎无法解决的。比如将会贯穿本课程的一个小项目:机器实现图片英文字母识别，你几乎无法找到一个能手工编写出的算法去让程序能够识别出英文字母.为什么这个问题难以手工编程解决？一种思考的角度是，对于上图这样2255尺寸的图片，如果将它看成是一个向量，向星中的每一个数值对应图片中的一个像素灰度值，那这个向量的维度（长度）就达到了2255=1210，相对于计算圆面积问题中长度为1的输入向量，这个向星维度非常高，存在于1210维的向量空间当中。高维度带来的高复杂度使得实际上无法使用手工编程解决这个问题。只有通过让机器自己“学习”，才有可能找到解决办法。<h3 id="2-2几个基本概念"><a href="#2-2几个基本概念" class="headerlink" title="2.2几个基本概念"></a>2.2几个基本概念</h3><h4 id="2-2-1回归问题与分类问题"><a href="#2-2-1回归问题与分类问题" class="headerlink" title="2.2.1回归问题与分类问题"></a>2.2.1回归问题与分类问题</h4></li>
<li>回归(regression)与分类(classification)是机器学习中的两大类问题。上面我们举的计算圆形面积的例子就属于<strong>回归问题</strong>，即我们的目的是对于一个输入x，预测其输出值y，且这个y值是根据×连续变化的值。分类问题则是事先给定若千个类别，对于一个输入x，判断其属于哪个类别，即输出一般是离散的，比如本课程将会带大家实践的图片英文字母识别就属于分类问题（判断一个图片中包含的字符属于26个字母中的哪一个)。<h4 id="2-2-2有监督学习和无监督学习"><a href="#2-2-2有监督学习和无监督学习" class="headerlink" title="2.2.2有监督学习和无监督学习"></a>2.2.2有监督学习和无监督学习</h4></li>
<li>有监督与无监督:是机器学习方法的两大分类。上面我们举的计算圆形面积的例子就属于有监督学习，因为我们的输入data既包含输入x，又包含x对应的y，即学习数据已经事先给出了正确答案。无监督学习则是只有输入x。你可能会感到不可思议，正确答案都不告诉我，我要怎么学习呢?确实，无监督学习要更难。无监督学习目前一般用于聚类(cluster)问题，即给定一批数据，根据这批数据的特点，将其分为多个类别，虽然我并不知道这每个类别所代表的具体含义。比如网络商城的商品推荐算法可能会根据用户的使用习惯，以往的浏览历史等，将用户分为多个类别，同一类别的用户在<h3 id="2-3模型的构建—神经网络"><a href="#2-3模型的构建—神经网络" class="headerlink" title="2.3模型的构建—神经网络"></a>2.3模型的构建—神经网络</h3></li>
<li>上面我们提到过要让机器“学习”，一般需要∶<ul>
<li>1.用来解决问题的模型model</li>
<li>2.学习数据（或者说训练数据）data</li>
<li>3.让模型model通过数据data学会解决特定问题的学习算法learn</li>
</ul>
</li>
<li>我们先来学习深度学习中一种非常重要的模型—神经网络(neural network )。<h4 id="2-3-1与生物大脑的类比"><a href="#2-3-1与生物大脑的类比" class="headerlink" title="2.3.1与生物大脑的类比"></a>2.3.1与生物大脑的类比</h4></li>
<li>一些人喜欢将生物大脑神经元网络的结构看做是神经网络的灵感来源。大脑神经元的结构如下:<img src="fff.png" alt="fff"></li>
<li>我们主要关注细胞体、树突、轴突。我们看到，树突作为接收端，接收从其他神经元传导过来的神经冲动，而轴突再将经过细胞体“处理”的神经冲动传递给其他神经元。<br>你会发现这张图片和上图大脑神经元的图片有很多相似之处，只不过神经冲动在这里变成了具体的数值。图中的圆圈代表一个个神经元(neuron)，其中网络层1(输入层)中的每个神经元都与网络层2中的每个神经元相连。同时我们注意到，神经元间相连的线上有该条连接线的权重w,神经网络工作时，将前一层网络中的每个神经元的值，与连接线上的权重w相乘，传递给下一层网络中的神经元。同时，每个神经元还会接收一个偏移量(bias)。即在该图中，有:<br>w11<em>a1+w12</em>a2+w13<em>a3 + bias1=b1<br>w21</em>a1+w22<em>a2+w23</em>a3 + bias2=b2<br>如果写成矩阵的形式(如果你不知道矩阵运算法则，可以先跳过这里，之后的实验会有讲解），就是︰<br>如果你对线性代数比较熟悉，你会发现这就是一个线性方程组而已，没错，线性代数是神经网络的重要理论基础之一，线性代数与神经网络的联系更密切直接。但是神经网络中不止有线性运算。对于网络层2中的神经元b，它的值在传给输出之前,会先经过一次非线性运算g(图中没有画出来)。即在该图中，有:<br>g(b1)=Y1<br>g(b2)=Y2<br>注:如果你不知道线性运算和非线性运算的区别，请自行查阅资料了解。<h4 id="2-3-2非线性激活函数"><a href="#2-3-2非线性激活函数" class="headerlink" title="2.3.2非线性激活函数"></a>2.3.2非线性激活函数</h4></li>
<li>神经网络中，将上面提到的非线性运算g称为激活函数。</li>
<li><p>实际运用当中，有多种激活函数可以选择，这里我们介绍最经典的一种激活函数:sigmoid激活函数，它的数学形式为:</p>
<script type="math/tex; mode=display">
g(x)=\frac{1}{1+e^{-x}}</script></li>
<li><p>即输入值x越大，函数值越接近1，输入值越小，函数值越接近0，当输入为0的时候，函数值为0.5。也许你会觉得奇怪，为什么我们需要这样一个复杂(但以后你会发现其实它的形式很简单优美）的函数来对网络层2的输出值再进行一遍运算呢?</p>
</li>
<li>有多种理由要求我们必须向神经网络中引入非线性运算部分，最简单的原因之一就是，对于如下图所示的多层神经网络(即深度神经网络），如果没有非线性运算部分，那么由于·矩阵乘法的结合性（这里又一次涉及到了矩阵的运算性质，如果你不熟悉又想搞清楚这里，请先看看第二次实验),多个线性运算层直接相连的效果实际上相当于只进行了—次线性运算，多层次的网络结构失去了意义。<img src="323.png" alt="323"></li>
<li>深度神经网络示例,图中非关键部分被略去。</li>
<li>如果没有非线性激活函数，则其中网路层1与网络层2之间的尺寸为2x3的矩阵和网络层2与网络层3之间的尺寸为3x2的变换矩阵由于矩阵乘法的结合性实际上会合并成为单独的一个3x3的矩阵（请思考这里为什么是3x3而不是2x2)。</li>
<li>第二个原因解释起来可能复杂一点，在解释第二个原因之前，让我们先看一看如果只有线性矩阵运算部分，我们的神经网络实际上在做些什么。以神经网络示例图中的b1为例，假如我们将其用于分类问题，同时只考虑有两个输入变量的情况，即: w11<em>a1+w12</em>a2 + bias1=b1对于输入a，当最终得到的b1大于0时，我们认为a属于b1所代表的的类别，否则不属于b1所代表的的类别。</li>
<li>对于式子w11<em>a1+w12</em>a2 + bias1=b1，其实你对他非常熟悉，如果在坐标轴上画出他的函数图形，就是这样的一条直</li>
<li>对于图中红色的点，其对应的b1值要大于0，可以认为其属于b1所代表的类别，而绿色的点对应的b1值小于0，故它们不属于b1所代表的类别。当输入数据的维度为3时，单纯的线性运算就相当于是使用一个平面对空间进行划分，当数据维度高于3维时类似，只是我们已经无法直观的观察大于3维的空间了。也许你马上就会发现一个问题，如果我们要分类的数据分布是下面这样的该怎么办:</li>
<li>不同类别之间的分界线不是直线而是曲线，此时无论如何也无法用线性分类器去准确的进行分类。而非线性部分的引入，在一定程度上可以使原本的直线变成曲线，如下图:<br>这样原本不可分的问题就变得可分了。<br>非线性激活函数还有其他作用，比如sigmoid将输出值的范围限制在0到1之间，使其刚好可以被作为概率来看待。<h3 id="2-4评价神经网络的效果"><a href="#2-4评价神经网络的效果" class="headerlink" title="2.4评价神经网络的效果"></a>2.4评价神经网络的效果</h3><h4 id="2-4-1使用随机值初始化神经网络参数"><a href="#2-4-1使用随机值初始化神经网络参数" class="headerlink" title="2.4.1使用随机值初始化神经网络参数"></a>2.4.1使用随机值初始化神经网络参数</h4></li>
<li>上一节中，我们只向大家介绍了神经网络的结构，我们现在还不知道如何设定神经网络中的网络参数值（包括连接线上的权重w和偏置值bias )。一种最简单的方法是随机设定网络参数。如果神经网络中的网络参数是随机设置的，那么我们得到的“网络性能”应该就是随机情况下的平均值。比如我们要判断一些图片中的字母属于26个字母中的哪一个，如果网络参数值是随机设定的话，那我们得到的识别准确率理论上应该在1/26左右。随机设定参数值当然不可能是我们解决问题的办法，但要学习如何设置“好”的网络参数值来使神经网络正确的工作，我们要先研究一下上面提到的“网络性能”的问题。我们如何判定一个神经网络是“好”的还是“不好”的呢，即我们用什么来评价一个神经网络的效果呢?<h4 id="2-4-2损失函数的概念"><a href="#2-4-2损失函数的概念" class="headerlink" title="2.4.2损失函数的概念"></a>2.4.2损失函数的概念</h4></li>
<li>对于有监督学习，由于我们的学习数据data中包含输入数据的预期正确输出值，一个简单的办法就是比较神经网络的输出 h与预期的正确输出y之间的差异。比如计算(h-y)^2，当得到的值比较大时，就说明我们的神经网络的输出与预期的正确输出偏差较大，反之，如果得到的值很小甚至等于О，就说明我们的模型工作的不错，能够正确的预测输出值。</li>
<li>这就引出了损失函数〈cost function〉的概念，实际当中同样有多种损失函数可供选择，这里我们来介绍最经典的-种损失函数:二次损失函数（ quadratic loss function） 。<h4 id="2-4-3二次损失函数quadratic-loss"><a href="#2-4-3二次损失函数quadratic-loss" class="headerlink" title="2.4.3二次损失函数quadratic loss"></a>2.4.3二次损失函数quadratic loss</h4></li>
<li><p>就像它的名字所暗示的那样，quadratic loss function通过计算h和y之间差值的二次方，来表达一个神经网络的效果。具体形式如下:</p>
<script type="math/tex; mode=display">
J0) =(h(0,X)-Y)2</script></li>
<li><ul>
<li>这个公式几乎就是我们在上面提到的对模型的误差求平方，只是稍微再复杂一点。其中的希腊字母theta代表网络中的所有参数，x代表向神经网络输入的数据，v代表输入数据对应的预期正确输出值。</li>
</ul>
</li>
<li>不知你是否注意到，这个公式有些奇怪，因为一般我们都是把变量名写入函数名后面的括号里，代表这个函数的自变量有哪些，可这里的网络参数theta怎么跑进去了，而训练数据×和v却不在里面。</li>
<li>这个地方有些微妙，而且非常重要。实际上，在这里theta才是自变量，因为我们一开始不知道让网络能够正确的工作的参数值是多少。我们的学习算法learn按照某种策略，通过不断的更新参数值来使损失函数7(theta,X, Y)的值减小。在这里， theta是不断变化的量。</li>
<li>那x和v就不是自变量了吗?对，它们确实不是自变量!因为对于我们要解决的一个具体的问题(（比如图片字母识别），其训练数据中包含的信息其实代表的是这个问题本身的性质，我们的学习算法learn其实最终就是要从训练数据data中学习到这些性质，并通过网络模型model的结构和其中的参数把这些性质表达出来。对于一个特定的问题来说，可以认为它们是常量!</li>
<li>这里比较容易搞混，请仔细思考一下theta和×v在这里所代表的意义。</li>
</ul>
<h3 id="2-5梯度下降算法"><a href="#2-5梯度下降算法" class="headerlink" title="2.5梯度下降算法"></a>2.5梯度下降算法</h3><ul>
<li>上一节我们介绍了衡量神经网络性能的办法:损失函数，那为了让我们的神经网络能够准确的识别图片中的字母，就必须想办法让损失函数的值尽量小，最好是让损失函数值为0。那么如何实现这个目标呢?这里就要佩服数学家的智慧（但是放心，这里不会涉及很高深的数学知识)。很久以前就有数学家发明了梯度下降算法来帮助我们减小损失函数的值。假设我们的损失函数图形是这样的:</li>
</ul>
<p><img src="eefs.png" alt="eefs"></p>
<ul>
<li>我们随机初始化的网络参数在A这个位置。显然，我们的目标是使A尽星靠近最低点B，最好是与B重合，这样才能最小化损失函数。这时，就必须介绍一下梯度的概念，如果你还记得高等数学中梯度的概念，可以直接跳过下一段。上图这种，函数图形向下突出的函数被称为“凸函数”（注意这里的叫法，“凸”是指向下凸)。<h4 id="2-5-1什么是“梯度”"><a href="#2-5-1什么是“梯度”" class="headerlink" title="2.5.1什么是“梯度”"></a>2.5.1什么是“梯度”</h4></li>
<li>梯度本身是一个向量，由损失函数对每个自变量(图中的 theta0和theta1 )分别求偏导(如果你不知道什么是偏导，可以先看看第二次试验中的相关讲解）得到。</li>
<li>形象的理解，梯度指向损失函数变化最快的那个方向(且该方向让函数值变大)，对于上图中三维的情形，就是曲面上在A点最“陡峭”的方向。对于二维的情形，其实就是斜率方向。<h4 id="2-5-2梯度下降算法"><a href="#2-5-2梯度下降算法" class="headerlink" title="2.5.2梯度下降算法"></a>2.5.2梯度下降算法</h4></li>
<li><p>那为了使A点向B点移动，就可以对A的值减去（请思考这里为什么是减去）该点的梯度。通过公式来表达，就是︰</p>
<script type="math/tex; mode=display">
\theta =\theta -\alpha \frac{\mathrm{d} J}{\mathrm{d} \theta}</script></li>
<li><p>注意这里多了一个希腊字母alpha，因为梯度只为我们指明了更新参数theta的方向，而我们具体朝着这个方向“走多远”则由alpha控制。当走到下一个位置之后，再求该点的梯度，用梯度更新参数位置，如此重复，直到逼近B点。这就是梯度下降算法的原理。这里你可能会有疑问，如果我们的损失函数不是凸函数怎么办，那样我们的梯度下降算法就可能无法达到最低点。或者我们可能被“局部最低点”（梯度为0 )欺骗而无法达到“全局最低点”。的确，这看起来是一个严重的问题，但实际当中，这个问题并没有那么突出，我们有一些办法来防止被“局部最低点”欺骗，后面的课程可能会讲到。</p>
</li>
<li>注: alpha在这里是一个超参数( hyperparameter )</li>
</ul>
<h3 id="2-6参数-parameter-与超参数-hyper-parameter-2-6-1区别"><a href="#2-6参数-parameter-与超参数-hyper-parameter-2-6-1区别" class="headerlink" title="2.6参数(parameter)与超参数(hyper parameter)2.6.1区别"></a>2.6参数(parameter)与超参数(hyper parameter)2.6.1区别</h3><ul>
<li>如果你足够仔细，你会发现在上面的描述中，我们将模型model中的参数theta称为参数，而学习算法（即梯度下降算法）里的参数alhpa被称为超参数。这两个地方的“参数”为什么叫法不一样呢?</li>
<li>叫法不同，是因为它们的作用及我们设置它们值的方式不一样。theta被称为“参数”，是因为theta决定了我们的模型model的性质，并且theta的最终值是由我们的学习算法learn学习得到的，不需要我们手工设定。而alpha则不同，在这里alpha并不是模型model中的参数，而是在学习算法learn 中决定我们的梯度下降算法每一步走多远的参数，它需要我们手工设定，且决定了得到最优theta的过程。即超参数决定如何得到最优参数。</li>
<li>看到这里你也许会说，这不还是要手动设置参数吗?虽然这里的超参数仍然需要我们手工设定，但模型中的数量巨大(对于复杂问题，模型中可能有上亿个参数!)的参数已经不需要我们来设定。这已经大大的减轻了手动设置参数的负担。所以说机器学习还是非常非常强大的。<h4 id="2-6-1超参数alpha—学习速率"><a href="#2-6-1超参数alpha—学习速率" class="headerlink" title="2.6.1超参数alpha—学习速率"></a>2.6.1超参数alpha—学习速率</h4></li>
<li>机器学习中，我们将上面提到的超参数alpha称为学习速率〈learning rate)。这很好理解，因为 alpha越大时，我们的参数theta更新的幅度越大，我们可能会更快的到达最低点。但是alpha不能设置的太大，否则有可能一次变化的太大导致“步子太长”，会直接越过最低点，甚至导致损失函数值不降反升。如下图:<br><img src="sdaf.png" alt="sdaf"></li>
</ul>
<p>所以，设置合理的学习速率alpha值非常重要。实际当中的做法一般是先设置alpha为一个较小的值，比如0.001,观察每一次参数theta更新后损失函数1的值如何变化，如果7变大了，就将alpha的值除以10，变成0.0001，直到损失函数值开始变小。如果,变小了，则可以将alpha再乘以10，使得学习的速率更快。最终使alpha停留在一个能够使变小，又不会因为值过小导致学得太慢的“临界值”。<br>深度学习中还可能有其他几个超参数，我们会在后面的课程继续介绍<br>2.7遗留的问题<br>我们已经介绍了深度学习中的网络模型model、学习算法learn，还顺便提了一下有监督学习下的训练（学习）数据data。可以说，到这里，深度学习中几个最基本最重要的概念你已经清楚了。<br>但是这里还留有一个严重的问题，上面我们提到梯度下降算法根据损失函数的梯度来更新模型参数，那么我们该如何求梯度呢?这个问题将留到我们第三次实验课程来解决，我门同时会开始编写代码（讲了这么多理论，终于可以写代码了)来实现梯度的求解。<br>第二次实验我们会讲解深度学习必须要用到的一些基本数学知识以及numpy科学计算库的用法。</p>
<p>四、课后作业<br>快来做下面的作业检验一下你理解的如何吧，所有的作业都能根据上面的知识点推测出答案。1.“手工编程计算圆的面积”中，3.14是“参数”还是“超参数”?<br>2.一个如本次实验所描述的神经网络有两层，第一层有10个神经元，第二层有20个神经元，请问这两层神经元之间有多少条连接?第一层到第二层之间由这些连接所表示的线性变换矩阵尺寸是多少?<br>3．“使用深度学习预测股票走势曲线”是分类问题还是回归问题?<br>4.“根据人脸图片识别人的性别”是分类问题还是回归问题?<br>5.如果说，有监督学习的训练数据data由输入x和正确答案v组成，那无监督学习的训练数据应该是什么样的?6.[选做题]sigmoid函数对×求导结果是什么?<br>7.[选做题]2.6.1小节中的图片里，为什么“学习速率适中”图中每次更新的“步子”越来越短，而“学习速率过大”图中每次更新的“步子”越来越长?<br>8.[选做题]你能自己想出一种求损失函数梯度的方法吗?</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5/" data-id="cl403sxsc003uf8vua4jghz18" data-title="深度学习基本概念" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-2-3-6" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/01/02/2-3-6/" class="article-date">
  <time class="dt-published" datetime="2022-01-02T14:51:50.000Z" itemprop="datePublished">2022-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/">操作系统基础知识</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/notes-on-computer-expertise/2022/01/02/2-3-6/">2.3.6</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="2-3-6本节小结"><a href="#2-3-6本节小结" class="headerlink" title="2.3.6本节小结"></a>2.3.6本节小结</h1><h2 id="大题"><a href="#大题" class="headerlink" title="大题"></a>大题</h2><h3 id="1-为什么要引入进程同步的概念"><a href="#1-为什么要引入进程同步的概念" class="headerlink" title="1)为什么要引入进程同步的概念?"></a>1)为什么要引入进程同步的概念?</h3><ul>
<li>在多道程序共同执行的条件下，进程与进程是并发执行的，不同进程之间存在不同的相互制约关系。为了协调进程之间的相互制约关系，引入了进程同步的概念。<h3 id="2-不同的进程之间会存在什么关系"><a href="#2-不同的进程之间会存在什么关系" class="headerlink" title="2)不同的进程之间会存在什么关系?"></a>2)不同的进程之间会存在什么关系?</h3></li>
<li>进程之间存在同步与互斥的制约关系。</li>
<li>同步是指为完成某种任务而建立的两个或多个进程，这些进程因为需要在某些位置上协调它们的工作次序而等待、传递信息所产生的制约关系。互斥是指当一一个进程进入临界区使用临界资源时，另一个进程必须等待，当占用临界资源的进程退出临界区后，另进程才允许去访问此临界资源。<h3 id="3-当单纯用本节介绍的方法解决这些问题时会遇到什么新的问题吗"><a href="#3-当单纯用本节介绍的方法解决这些问题时会遇到什么新的问题吗" class="headerlink" title="3)当单纯用本节介绍的方法解决这些问题时会遇到什么新的问题吗?"></a>3)当单纯用本节介绍的方法解决这些问题时会遇到什么新的问题吗?</h3></li>
<li>当两个或两个以上的进程在执行过程中，因占有一些资源而又需要对方的资源时，会因为争夺资源面造成种互相等待的现象， 若无外力作用，它们都将无法推进下去。这种现象称为死铺，</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/2-3-6/" data-id="cl403sxqe0003f8vu0lmegspp" data-title="2.3.6" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-2-2-7" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/01/02/2-2-7/" class="article-date">
  <time class="dt-published" datetime="2022-01-02T08:17:12.000Z" itemprop="datePublished">2022-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/">操作系统基础知识</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/notes-on-computer-expertise/2022/01/02/2-2-7/">2.2.6</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="2-2-7本节习题精选"><a href="#2-2-7本节习题精选" class="headerlink" title="2.2.7本节习题精选"></a>2.2.7本节习题精选</h1><h2 id="一，单项选择题"><a href="#一，单项选择题" class="headerlink" title="一，单项选择题"></a>一，单项选择题</h2><h3 id="1-时间片轮转调度算法是为了-。"><a href="#1-时间片轮转调度算法是为了-。" class="headerlink" title="1. 时间片轮转调度算法是为了( )。"></a>1. 时间片轮转调度算法是为了( )。</h3><ul>
<li>A.多个用户能及时干预系统</li>
<li>B.使系统变得高效</li>
<li>C.优先级较高的进程得到及时响应</li>
<li>D.需要CPU时间最少的进程最先做<h3 id="2-在单处理器的多进程系统中，进程什么时候占用处理器及决定占用时间的长短是由-决定的。"><a href="#2-在单处理器的多进程系统中，进程什么时候占用处理器及决定占用时间的长短是由-决定的。" class="headerlink" title="2. 在单处理器的多进程系统中，进程什么时候占用处理器及决定占用时间的长短是由( )决定的。"></a>2. 在单处理器的多进程系统中，进程什么时候占用处理器及决定占用时间的长短是由( )决定的。</h3></li>
<li>A.进程相应的代码长度</li>
<li>B.进程总共需要运行的时间</li>
<li>C.进程特点和进程调度策略</li>
<li>D.进程完成什么功能<h3 id="3-有利于CPU繁忙型的作业，而不利于I-0繁忙型的作业。"><a href="#3-有利于CPU繁忙型的作业，而不利于I-0繁忙型的作业。" class="headerlink" title="3.()有利于CPU繁忙型的作业，而不利于I/0繁忙型的作业。"></a>3.()有利于CPU繁忙型的作业，而不利于I/0繁忙型的作业。</h3></li>
<li>A.时间片轮转调度算法</li>
<li>B.先来先服务调度算法</li>
<li>C.短作业(进程)优先算法</li>
<li>D.优先权调度算法<h3 id="4-下面有关选择进程调度算法的准则中，-不正确的是"><a href="#4-下面有关选择进程调度算法的准则中，-不正确的是" class="headerlink" title="4. 下面有关选择进程调度算法的准则中， 不正确的是( )."></a>4. 下面有关选择进程调度算法的准则中， 不正确的是( ).</h3></li>
<li>A.尽快响应交互式用户的请求</li>
<li>B.尽量提高处理器利用率</li>
<li>C.尽可能提高系统吞吐量</li>
<li>D.适当增长进程就绪队列的等待时间<h3 id="5-设有4个作业同时到达，每个作业的执行时间均有2h，在一台处理器上按单道式运行，则平均周转时间为"><a href="#5-设有4个作业同时到达，每个作业的执行时间均有2h，在一台处理器上按单道式运行，则平均周转时间为" class="headerlink" title="5.设有4个作业同时到达，每个作业的执行时间均有2h，在一台处理器上按单道式运行，则平均周转时间为( )."></a>5.设有4个作业同时到达，每个作业的执行时间均有2h，在一台处理器上按单道式运行，则平均周转时间为( ).</h3></li>
<li>A.1h</li>
<li>B. 5h</li>
<li>C.2.5h</li>
<li>D. 8h</li>
</ul>
<h2 id="大题"><a href="#大题" class="headerlink" title="大题"></a>大题</h2><h3 id="1-为什么要进行处理机调度"><a href="#1-为什么要进行处理机调度" class="headerlink" title="1)为什么要进行处理机调度?"></a>1)为什么要进行处理机调度?</h3><p>若没有处理机调度，同意味者要等到当前运行的进程执行完毕后，下一个进程才能执行，而实际情况中，进程时常需要等待一些外部设备的输入，而外部设备的速度与处理机相比是非常缓慢的，若让处理机总是等待外部设备，则对处理机的资源是极大的浪费。而引进处理机调度后，可在运行进程等待外部设备时，把处理机调度给其他进程，从而提高处理机的利用率。用一句简单的话说，就是为了合理地处理计算机的软/硬件资源。</p>
<h3 id="2-调度算法有哪几种-结合第1章学习的分时操作系统和实时操作系统，思考有没有哪种调度算法比较适合这两种操作系统。"><a href="#2-调度算法有哪几种-结合第1章学习的分时操作系统和实时操作系统，思考有没有哪种调度算法比较适合这两种操作系统。" class="headerlink" title="2)调度算法有哪几种?结合第1章学习的分时操作系统和实时操作系统，思考有没有哪种调度算法比较适合这两种操作系统。"></a>2)调度算法有哪几种?结合第1章学习的分时操作系统和实时操作系统，思考有没有哪种调度算法比较适合这两种操作系统。</h3><p>本节介绍的调度算法有先来先服务调度算法、短作业优先调度算法、优先级调度算法、高响应比优先调度算法、时间片轮转调度算法、多级反馈队列调度算法6种。</p>
<p>先来先服务算法和短作业优先算法无法保证及时地接收和处理问题，因此无法保证在规定的时间间隔内响应每个用户的需求，也同样无法达到实时操作系统的及时性需求。优先级调度算法<br>按照任务的优先级进行调度，对于更紧急的任务给予更高的优先级，适合实时操作系统。</p>
<p>高响应比优先调度算法、时间片轮转调度算法、多级反馈队列调度算法都能保证每个任务在一定时间内分配到时间片， 并轮流占用CPU,适合分时操作系统。<br>本节主要介绍了处理机调度的概念。操作系统主要管理处理机、内存、文件、设备几种资源，只要对资源的请求大于资源本身的数量，就会涉及调度。例如，在单处理机系统中，处理机只有一个， 而请求服务的进程却有多个，所以就有处理机调度的概念出现。而出现调度的概念后，又有了一个问题，即如何调度、应该满足谁、应该让谁等待，这是调度算法所回答的问题:而应该满足谁、应该让谁等待，要遵循一定的准则，即调度的准则。调度这一概念贯穿于操作系统的始终，读者在接下来的学习中，将接触到几种资源的调度问题和相应的调度算法。将它们与处理机调度的内容相对比，将会发现它们有异曲同工之妙。<br>本节的知识架构图如下:</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/2-2-7/" data-id="cl403sxqa0001f8vu8io0bam8" data-title="2.2.6" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-blender/blender" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/01/02/blender/blender/" class="article-date">
  <time class="dt-published" datetime="2022-01-02T08:17:12.000Z" itemprop="datePublished">2022-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/notes-on-computer-expertise/categories/blender/">blender</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/notes-on-computer-expertise/2022/01/02/blender/blender/">blender基础</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="blender"><a href="#blender" class="headerlink" title="blender"></a>blender</h1><h2 id="控制入门"><a href="#控制入门" class="headerlink" title="控制入门"></a>控制入门</h2><ul>
<li>按住滑鼠<ul>
<li>画面转动</li>
</ul>
</li>
<li>A<ul>
<li>全部/取消 勾选</li>
</ul>
</li>
<li>SHITH+滑鼠<ul>
<li>平移摄影机位置</li>
</ul>
</li>
<li>shift+c</li>
<li>准星回归<h2 id="马克杯制作"><a href="#马克杯制作" class="headerlink" title="马克杯制作"></a>马克杯制作</h2><h2 id="推挤"><a href="#推挤" class="headerlink" title="推挤"></a>推挤</h2><h2 id="进入编辑模式-Edit"><a href="#进入编辑模式-Edit" class="headerlink" title="进入编辑模式 Edit"></a>进入编辑模式 Edit</h2><h2 id="分支主题-3"><a href="#分支主题-3" class="headerlink" title="分支主题 3"></a>分支主题 3</h2></li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/blender/blender/" data-id="cl403sxqu000uf8vu8exdeeky" data-title="blender基础" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-2-1-8-进程的习题" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/01/02/2-1-8-%E8%BF%9B%E7%A8%8B%E7%9A%84%E4%B9%A0%E9%A2%98/" class="article-date">
  <time class="dt-published" datetime="2022-01-02T06:56:00.000Z" itemprop="datePublished">2022-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/">操作系统基础知识</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/notes-on-computer-expertise/2022/01/02/2-1-8-%E8%BF%9B%E7%A8%8B%E7%9A%84%E4%B9%A0%E9%A2%98/">2.1.8 进程的习题</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h2 id="2-1-8-进程"><a href="#2-1-8-进程" class="headerlink" title="2.1.8 进程"></a>2.1.8 进程</h2><h3 id="一、单项选择题"><a href="#一、单项选择题" class="headerlink" title="一、单项选择题"></a>一、单项选择题</h3><ul>
<li><ul>
<li>1.一个进程映像是()。<ul>
<li>A.由协处理器执行的一个程序</li>
<li>B.一个独立的程序＋数据集</li>
<li>C. PCB结构与程序和数据的组合</li>
<li>D.一个独立的程序</li>
</ul>
</li>
<li>2．下列关于线程的叙述中,正确的是（)。<ul>
<li>A.线程包含CPU 现场,可以独立执行程序</li>
<li>B.每个线程有自己独立的地址空间</li>
<li>C.进程只能包含一个线程</li>
<li>D.线程之间的通信必须使用系统调用函数</li>
</ul>
</li>
</ul>
</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/2-1-8-%E8%BF%9B%E7%A8%8B%E7%9A%84%E4%B9%A0%E9%A2%98/" data-id="cl403sxq20000f8vue4tp6kvh" data-title="2.1.8 进程的习题" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-Spring开发/一、spring基础介绍" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/01/02/Spring%E5%BC%80%E5%8F%91/%E4%B8%80%E3%80%81spring%E5%9F%BA%E7%A1%80%E4%BB%8B%E7%BB%8D/" class="article-date">
  <time class="dt-published" datetime="2022-01-02T06:56:00.000Z" itemprop="datePublished">2022-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/notes-on-computer-expertise/categories/spring/">spring</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/notes-on-computer-expertise/2022/01/02/Spring%E5%BC%80%E5%8F%91/%E4%B8%80%E3%80%81spring%E5%9F%BA%E7%A1%80%E4%BB%8B%E7%BB%8D/">一、spring基础介绍</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="第一章-Spring"><a href="#第一章-Spring" class="headerlink" title="第一章    Spring"></a>第一章    Spring</h1><h2 id="1-1-简单描述-Spring"><a href="#1-1-简单描述-Spring" class="headerlink" title="1.1 简单描述 Spring"></a>1.1 简单描述 Spring</h2><ul>
<li>Spring是一个<strong>轻量级</strong>Java开发框架，目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。</li>
<li>它是一个分层的JavaSE/JavaEE full-stack（一站式）轻量级开源框架，为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构，因此Java开发者可以专注于应用程序的开发。</li>
<li>Spring最根本的使命是解决企业级应用开发的复杂性，即简化Java开发。</li>
<li>Spring有很多丰富的功能，但是这些功能的底层都依赖于它的两个核心特性，也就是依赖注入（dependency injection，DI）和面向切面编程（aspect-oriented programming，AOP）。<h2 id="1-2-简述Spring应用了哪些设计模式"><a href="#1-2-简述Spring应用了哪些设计模式" class="headerlink" title="1.2 简述Spring应用了哪些设计模式"></a>1.2 简述Spring应用了哪些设计模式</h2></li>
<li>答：Spring主要应用了以下<strong>5种</strong>设计模式：<ul>
<li>1.工厂模式：BeanFactory就是简单工厂模式的体现，用来创建对象的实例；</li>
<li>2.单例模式：Bean默认为单例模式。</li>
<li>3.代理模式：Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术；</li>
<li>4.模板方法：用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。</li>
<li>5.观察者模式：定义对象键一种一对多的依赖关系，当一个对象的状态发生改变时，所有依赖于它的对象都会得到通知被制动更新，如Spring中listener的实现–ApplicationListener。<h2 id="1-3-简述SpringContext"><a href="#1-3-简述SpringContext" class="headerlink" title="1.3 简述SpringContext"></a>1.3 简述SpringContext</h2></li>
</ul>
</li>
<li>SpringContenxt 是Spring核心容器，是基本的Spring模块，提供Spring框架的基础功能，BeanFactory 是任何以Spring为基础的应用的核心。Spring框架建立在此模块之上，它使Spring成为一个容器。</li>
<li>Bean 工厂是工厂模式的一个实现，提供了控制反转功能，用来把应用的配置和依赖从真正的应用代码中分离。最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ，它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创建一个完全配置的系统或应用。<h2 id="1-4-简述Spring-IOC"><a href="#1-4-简述Spring-IOC" class="headerlink" title="1.4 简述Spring IOC"></a>1.4 简述Spring IOC</h2></li>
<li>答：【重点】IoC (Inversion of Control) 就是控制反转，它把传统上由程序代码直接操控的对象的调用权交给容器，通过容器来实现对象组件的装配和管理。<ul>
<li>所谓的“控制反转”概念就是对组件对象控制权的转移，从程序代码本身转移到了外部容器。<ul>
<li>SpringIOC 负责创建对象<ul>
<li>管理对象（通过依赖注入（DI），</li>
<li>装配对象，</li>
<li>配置对象，</li>
<li>并且管理这些对象的整个生命周期。</li>
</ul>
</li>
</ul>
</li>
<li>控制反转(IoC)的作用——<ul>
<li>1．管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事，在对象关系比较复杂时，如果依赖关系需要开发人员来维护的话，还是很困难的。</li>
<li>2．解耦，由容器去维护具体的对象。</li>
<li>3．托管了类的产生过程，比如我们需要在类的产生过程中做一些处理，最直接的例子就是代理，如果有容器程序可以把这部分处理交给容器，应用程序则无需去关心类是如何完成代理的。</li>
</ul>
</li>
<li>IOC的优点是——<ul>
<li><ol>
<li>IOC或依赖注入把应用的代码量降到最低</li>
</ol>
</li>
<li><ol>
<li>它使应用容易测试，单元测试不再需要单例和JNDI查找机制</li>
</ol>
</li>
<li><ol>
<li>最小的代价和最小的侵入性使松散耦合得以实现</li>
</ol>
</li>
<li><ol>
<li>IOC容器支持加载服务时的饿汉式初始化和懒加载</li>
</ol>
</li>
</ul>
</li>
<li>Spring中的 IoC 的实现原理就是工厂模式加反射机制。<h2 id="1-5-简述Spring-IOC-提供哪些功能"><a href="#1-5-简述Spring-IOC-提供哪些功能" class="headerlink" title="1.5 简述Spring IOC 提供哪些功能"></a>1.5 简述Spring IOC 提供哪些功能</h2></li>
</ul>
</li>
<li>1.依赖注入<ul>
<li>从 XML 的配置上说，即 ref 标签。对应 SpringRuntimeBeanReference 对象。</li>
<li>对于 IoC 来说，最重要的就是容器。容器管理着 Bean 的生命周期，控制着 Bean 的依赖注入</li>
</ul>
</li>
<li>2.依赖检查</li>
<li>3.自动装配</li>
<li>4.支持集合</li>
<li>5.指定初始化方法和销毁方法</li>
<li>6.支持回调某些方法（但是需要实现 Spring接口，略有侵入）<h2 id="1-6-简述BeanFactory-和-ApplicationContext区别"><a href="#1-6-简述BeanFactory-和-ApplicationContext区别" class="headerlink" title="1.6 简述BeanFactory 和 ApplicationContext区别"></a>1.6 简述BeanFactory 和 ApplicationContext区别</h2></li>
<li>BeanFactory和ApplicationContext是Spring的两大核心接口，都可以做Spring的容器。其中ApplicationContext是BeanFactory的子接口。<h3 id="1-6-1-依赖关系"><a href="#1-6-1-依赖关系" class="headerlink" title="1.6.1 依赖关系"></a>1.6.1 依赖关系</h3></li>
<li>BeanFactory：是Spring里面最下层的接口，包含了各种Bean的定义，读取bean配置文档，管理bean的加载、实例化，控制bean的生命周期，维护bean之间的依赖关系。<ul>
<li>ApplicationContext接口作为BeanFactory的派生，除了提供BeanFactory所具有的功能外，还提供了更完整的框架功能：<ul>
<li>继承MessageSource，因此支持国际化。</li>
<li>统一的资源文件访问方式。</li>
<li>提供在监听器中注册bean的事件。</li>
<li>同时加载多个配置文件。</li>
<li>载入多个（有继承关系）上下文 ，使得每一个上下文都专注于一个特定的层次，比如应用的web层。<h3 id="1-6-2-加载方式"><a href="#1-6-2-加载方式" class="headerlink" title="1.6.2 加载方式"></a>1.6.2 加载方式</h3></li>
</ul>
</li>
</ul>
</li>
<li>BeanFactroy采用的是延迟加载形式来注入Bean的，即只有在使用到某个Bean时(调用getBean())，才对该Bean进行加载实例化。这样，我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入，BeanFacotry加载后，直至第一次使用调用getBean方法才会抛出异常。</li>
<li>ApplicationContext，它是在容器启动时，一次性创建了所有的Bean。这样，在容器启动时，我们就可以发现Spring中存在的配置错误，这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预加载入所有的单实例Bean，通过预载入单实例bean ,确保当你需要的时候，你就不用等待，因为它们已经创建好了。</li>
<li>相对于基本的BeanFactory，ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时，程序启动较慢。<h3 id="1-6-3-创建方式"><a href="#1-6-3-创建方式" class="headerlink" title="1.6.3 创建方式"></a>1.6.3 创建方式</h3></li>
<li>BeanFactory通常以编程的方式被创建，ApplicationContext还能以声明的方式创建，如使用ContextLoader。<h3 id="1-6-4-注册方式"><a href="#1-6-4-注册方式" class="headerlink" title="1.6.4 注册方式"></a>1.6.4 注册方式</h3></li>
<li>BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用，但两者之间的区别是：BeanFactory需要手动注册，而ApplicationContext则是自动注册。<h2 id="1-7-简述BeanFactory和FactoryBean的区别"><a href="#1-7-简述BeanFactory和FactoryBean的区别" class="headerlink" title="1.7 简述BeanFactory和FactoryBean的区别"></a>1.7 简述BeanFactory和FactoryBean的区别</h2></li>
<li>BeanFactory是个Factory，也就是IOC容器或对象工厂，FactoryBean是个Bean。在Spring中，所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。对FactoryBean而言，这个Bean不是简单的Bean，而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。<h2 id="1-8-Spring循环依赖问题解决原理"><a href="#1-8-Spring循环依赖问题解决原理" class="headerlink" title="1.8 Spring循环依赖问题解决原理"></a>1.8 Spring循环依赖问题解决原理</h2></li>
<li>Spring通过三级缓存解决了循环依赖<ul>
<li>一级缓存为单例池（singletonObjects）</li>
<li>二级缓存为早期曝光对象earlySingletonObjects</li>
<li>三级缓存为早期曝光对象工厂（singletonFactories）</li>
</ul>
</li>
<li>当A 、B两个类发生循环引用时，在A完成实例化后，就使用实例化后的对象去创建一个对象工厂，并添加到三级缓存中，如果A被AOP代理，那么通过这个工厂获取到的就是A代理后的对象，如果A没有被AOP代理，那么这个工厂获取到的就是A实例化的对象。当A进行属性注入时，会去创建B，同时B又依赖了A，所以创建B的同时又会去调用getBean(a)来获取需要的依赖，此时的getBean(a)会从缓存中获取<ul>
<li>第一步，先获取到三级缓存中的工厂；</li>
<li>第二步，调用对象工厂的getObject方法来获取到对应的对象，得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程，包括初始化、后置处理器等。当B创建完后，会将B再注入到A中，此时A再完成它的整个生命周期。至此，循环依赖结束<h2 id="1-9-详述BeanFactory和ApplicationContext"><a href="#1-9-详述BeanFactory和ApplicationContext" class="headerlink" title="1.9 详述BeanFactory和ApplicationContext"></a>1.9 详述BeanFactory和ApplicationContext</h2></li>
</ul>
</li>
<li>BeanFactory和ApplicationContext都是容器，BeanFactory 简单，可以理解为就是 HashMap，Key 是 BeanName，Value 是 Bean 实例。通常只提供注册（put），获取（get）这两个功能。可以称之为 “低级容器”。ApplicationContext 可以称之为 “高级容器”。因为它比 BeanFactory 多了更多的功能。它继承了多个接口。因此具备了更多的功能。例如资源的获取，支持多种消息（例如 JSP tag 的支持），对 BeanFactory 多了工具级别的支持等待。所以你看它的名字，已经不是 BeanFactory 之类的工厂了，而是 “应用上下文”， 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法，此方法是所有阅读 Spring源码的人的最熟悉的方法，用于刷新整个容器，即重新加载/刷新所有的 bean。</li>
<li>IoC 在 Spring里，只需要低级容器就可以实现，2个步骤：<ul>
<li>1.加载配置文件，解析成 BeanDefinition 放在 Map 里。</li>
<li>2.调用 getBean 的时候，从 BeanDefinition 所属的 Map 里，拿出 Class 对象进行实例化，同时，如果有依赖关系，将递归调用 getBean 方法 —— 完成依赖注入。</li>
</ul>
</li>
<li>上面就是 Spring低级容器（BeanFactory）的 IoC。</li>
<li>至于高级容器 ApplicationContext，它包含了低级容器的功能，当它执行 refresh 模板方法的时候，将刷新整个容器的 Bean。同时其作为高级容器，包含了太多的功能。一句话，它不仅仅是 IoC。它支持不同信息源头，支持 BeanFactory 工具类，支持层级容器，支持访问文件资源，支持事件发布通知，支持接口回调等等。<h2 id="1-10-简述ApplicationContext的具体实现类"><a href="#1-10-简述ApplicationContext的具体实现类" class="headerlink" title="1.10 简述ApplicationContext的具体实现类"></a>1.10 简述ApplicationContext的具体实现类</h2></li>
<li><ol>
<li>FileSystemXmlApplicationContext ：此容器从一个XML文件中加载beans的定义，XML Bean 配置文件的全路径名必须提供给它的构造函数。</li>
</ol>
</li>
<li>2.ClassPathXmlApplicationContext：此容器也从一个XML文件中加载beans的定义，这里，你需要正确设置classpath因为这个容器将在classpath里找bean配置。</li>
<li>3.WebXmlApplicationContext：此容器加载一个XML文件，此文件定义了一个WEB应用的所有bean。<h2 id="1-11-简述Spring的依赖注入"><a href="#1-11-简述Spring的依赖注入" class="headerlink" title="1.11 简述Spring的依赖注入"></a>1.11 简述Spring的依赖注入</h2></li>
<li>控制反转IoC主要实现方式有两种：依赖注入和依赖查找<ul>
<li>依赖注入<ul>
<li>含义：相对于IoC而言，依赖注入(DI)更加准确地描述了IoC的设计理念。所谓依赖注入（Dependency Injection），即组件之间的依赖关系由容器在应用系统运行期来决定，也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询，只提供普通的Java方法让容器去决定依赖关系。</li>
<li>1.11.1 依赖注入的基本原则<ul>
<li>依赖注入的基本原则是：应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责，“查找资源”的逻辑应该从应用组件的代码中抽取出来，交给IoC容器负责。容器全权负责组件的装配，它会把符合依赖关系的对象通过属性（JavaBean中的setter）或者是构造器传递给需要的对象。</li>
</ul>
</li>
<li>1.11.2 依赖注入有什么优势<ul>
<li>依赖注入之所以更流行是因为它是一种更可取的方式：让容器全权负责依赖查询，受管组件只需要暴露JavaBean的setter方法或者带参数的构造器或者接口，使容器可以在初始化时组装对象的依赖关系。</li>
</ul>
</li>
</ul>
</li>
<li>其与依赖查找方式相比，主要优势为：<ul>
<li>查找定位操作与应用代码完全无关。</li>
<li>不依赖于容器的API，可以很容易地在任何容器以外使用应用对象。</li>
<li>不需要特殊的接口，绝大多数对象可以做到完全不必依赖容器。</li>
<li>1.11.3 有哪些不同类型的依赖注入实现方式？<ul>
<li>依赖注入是时下最流行的IoC实现方式，依赖注入分为接口注入（Interface Injection），Setter方法注入（Setter Injection）和构造器注入（Constructor Injection）三种方式<ul>
<li>接口注入<ul>
<li>由于在灵活性和易用性比较差，现在从Spring4开始已被废弃。</li>
</ul>
</li>
<li>构造器依赖注入<ul>
<li>构造器依赖注入通过容器触发一个类的构造器来实现的，该类有一系列参数，每个参数代表一个对其他类的依赖。</li>
</ul>
</li>
<li>Setter方法注入<ul>
<li>Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后，调用该bean的setter方法，即实现了基于setter的依赖注入。</li>
</ul>
</li>
</ul>
</li>
<li>6.2.4构造器依赖注入和 Setter方法注入的区别<ul>
<li>构造函数注入<ul>
<li>没有部分注入</li>
<li>不会覆盖 setter 属性</li>
<li>任意修改都会创建一个新实例</li>
<li>适用于设置很多属性</li>
</ul>
</li>
<li>setter 注入<ul>
<li>有部分注入</li>
<li>会覆盖 setter 属性</li>
<li>任意修改不会创建一个新实例</li>
<li>适用于设置少量属性<h2 id="1-12-简述Spring-beans"><a href="#1-12-简述Spring-beans" class="headerlink" title="1.12 简述Spring beans"></a>1.12 简述Spring beans</h2></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Spring beans 是那些形成Spring应用的主干的java对象。它们被SpringIOC容器初始化，装配和管理。这些beans通过容器中配置的元数据创建。比如，以XML文件中的形式定义<h2 id="1-13-简述Spring-Bean的作用域"><a href="#1-13-简述Spring-Bean的作用域" class="headerlink" title="1.13 简述Spring Bean的作用域"></a>1.13 简述Spring Bean的作用域</h2></li>
</ul>
</li>
<li>当定义一个 在Spring里，我们还能给这个bean声明一个作用域。它可以通过bean 定义中的scope属性来定义。如，当Spring要在需要的时候每次生产一个新的bean实例，bean的scope属性被指定为prototype。另一方面，一个bean每次使用的时候必须返回同一个实例，这个bean的scope 属性 必须设为Singleton。</li>
<li>Spring框架支持以下五种bean的作用域：<ul>
<li>singleton : bean在每个Springioc 容器中只有一个实例。</li>
<li>prototype：一个bean的定义可以有多个实例。</li>
<li>request：每次http请求都会创建一个bean，该作用域仅在基于web的SpringApplicationContext情形下有效。</li>
<li>session：在一个HTTP Session中，一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext情形下有效。</li>
<li>global-session：在一个全局的HTTP Session中，一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext情形下有效。</li>
</ul>
</li>
<li>注意： 缺省的Springbean 的作用域是Singleton。使用 prototype 作用域需要慎重的思考，因为频繁创建和销毁 bean 会带来很大的性能开销。<h2 id="1-14-简述Spring框架中的单例bean是线程安全？"><a href="#1-14-简述Spring框架中的单例bean是线程安全？" class="headerlink" title="1.14 简述Spring框架中的单例bean是线程安全？"></a>1.14 简述Spring框架中的单例bean是线程安全？</h2></li>
<li>不是，Spring框架中的单例bean不是线程安全的。</li>
<li>Spring中的 bean 默认是单例模式，Spring框架并没有对单例 bean 进行多线程的封装处理。</li>
<li>实际上大部分时候 Spring bean 无状态的（比如 dao 类），所有某种程度上来说 bean 也是安全的，但如果 bean 有状态的话（比如 view model 对象），那就要开发者自己去保证线程安全了，最简单的就是改变 bean 的作用域，把“Singleton”变更为“prototype”，这样请求 bean 相当于 new Bean()了，所以就可以保证线程安全了。</li>
<li>有状态就是有数据存储功能。</li>
<li>无状态就是不会保存数据。<h2 id="1-15-简述Spring如何处理线程并发问题"><a href="#1-15-简述Spring如何处理线程并发问题" class="headerlink" title="1.15 简述Spring如何处理线程并发问题"></a>1.15 简述Spring如何处理线程并发问题</h2></li>
<li>答：在一般情况下，只有无状态的Bean才可以在多线程环境下共享，在Spring中，绝大部分Bean都可以声明为Singleton作用域，因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理，解决线程安全问题。</li>
<li>ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式，仅提供一份变量，不同的线程在访问前需要获取锁，没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。    </li>
<li>ThreadLocal会为每一个线程提供一个独立的变量副本，从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本，从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象，在编写多线程代码时，可以把不安全的变量封装进ThreadLocal。<h2 id="1-16-简述Spring框架中bean的生命周期"><a href="#1-16-简述Spring框架中bean的生命周期" class="headerlink" title="1.16 简述Spring框架中bean的生命周期"></a>1.16 简述Spring框架中bean的生命周期</h2></li>
<li>在传统的Java应用中，bean的生命周期很简单。使用Java关键字new进行bean实例化，然后该bean就可以使用了。一旦该bean不再被使用，则由Java自动进行垃圾回收。相比之下，Spring容器中的bean的生命周期就显得相对复杂多了。正确理解Springbean的生命周期非常重要，因为你或许要利用Spring提供的扩展点来自定义bean的创建过程。</li>
<li>bean在Spring容器中从创建到销毁经历了若干阶段，每一阶段都可以针对Spring如何管理bean进行个性化定制。如上图，在bean准备就绪之前，bean工厂执行了若干启动步骤。进行详细描述：Spring对bean进行实例化；Spring将值和bean的引用注入到bean对应的属性中；如果bean实现了BeanNameAware接口，Spring将bean的ID传递给setBean-Name()方法；如果bean实现了BeanFactoryAware接口，Spring将调用setBeanFactory()方法，将BeanFactory容器实例传入；如果bean实现了ApplicationContextAware接口，Spring将调用setApplicationContext()方法，将bean所在的应用上下文的引用传入进来；如果bean实现了BeanPostProcessor接口，Spring将调用它们的post-ProcessBeforeInitialization()方法；</li>
<li>如果bean实现了InitializingBean接口，Spring将调用它们的after-PropertiesSet()方法。</li>
<li>类似地，如果bean使用initmethod声明了初始化方法，该方法也会被调用；</li>
<li>如果bean实现了BeanPostProcessor接口，Spring将调用它们的post-ProcessAfterInitialization()方法；</li>
<li>此时，bean已经准备就绪，可以被应用程序使用了，它们将一直驻留在应用上下文中，直到该应用上下文被销毁；</li>
<li>如果bean实现了DisposableBean接口，Spring将调用它的destroy()接口方法。同样，如果bean使用destroy-method声明了销毁方法，该方法也会被调用。</li>
<li>现在已经了解了如何创建和加载一个Spring容器。但是一个空的容器并没有太大的价值，在把东西放进去之前，它里面什么都没有。为了从Spring的DI(依赖注入)中受益，必须将应用对象装配进Spring容器中。<h2 id="1-17-简述重要的bean生命周期方法，能重载？"><a href="#1-17-简述重要的bean生命周期方法，能重载？" class="headerlink" title="1.17 简述重要的bean生命周期方法，能重载？"></a>1.17 简述重要的bean生命周期方法，能重载？</h2></li>
<li>有两个重要的bean 生命周期方法，第一个是setup ， 它是在容器加载bean的时候被调用。第二个方法是 teardown 它是在容器卸载类的时候被调用。</li>
<li>bean 标签有两个重要的属性（init-method和destroy-method）。用它们你可以自己定制初始化和注销方法。它们也有相应的注解（@PostConstruct和@PreDestroy）。<h2 id="1-18-简述Spring的内部bean"><a href="#1-18-简述Spring的内部bean" class="headerlink" title="1.18 简述Spring的内部bean"></a>1.18 简述Spring的内部bean</h2></li>
<li>答：在Spring框架中，当一个bean仅被用作另一个bean的属性时，它能被声明为一个内部bean。内部bean可以用setter注入“属性”和构造方法注入“构造参数”的方式来实现，内部bean通常是匿名的，它们的Scope一般是prototype。<h2 id="1-19-简述bean的自动装配？"><a href="#1-19-简述bean的自动装配？" class="headerlink" title="1.19 简述bean的自动装配？"></a>1.19 简述bean的自动装配？</h2></li>
<li>答：在Spring框架中，在配置文件中设定bean的依赖关系是一个很好的机制，Spring容器能够自动装配相互合作的bean，这意味着容器不需要和配置，能通过Bean工厂自动处理bean之间的协作。这意味着 Spring可以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上，也可以设定在特定的bean上。<h2 id="1-20-简述Spring自动装配-bean方式"><a href="#1-20-简述Spring自动装配-bean方式" class="headerlink" title="1.20 简述Spring自动装配 bean方式"></a>1.20 简述Spring自动装配 bean方式</h2></li>
<li>答：在spring中，对象无需自己查找或创建与其关联的其他对象，由容器负责把需要相互协作的对象引用赋予各个对象，使用autowire来配置自动装载模式。</li>
<li>在Spring框架xml配置中共有5种自动装配：<ul>
<li>no：默认的方式是不进行自动装配的，通过手工设置ref属性来进行装配bean。</li>
<li>byName：通过bean的名称进行自动装配，如果一个bean的 property 与另一bean 的name 相同，就进行自动装配。</li>
<li>byType：通过参数的数据类型进行自动装配。</li>
<li>constructor：利用构造函数进行装配，并且构造函数的参数通过byType进行装配。</li>
<li>autodetect：自动探测，如果有构造方法，通过 construct的方式自动装配，否则使用 byType的方式自动装配。<h2 id="1-21-简述使用-Autowired注解自动装配的过程"><a href="#1-21-简述使用-Autowired注解自动装配的过程" class="headerlink" title="1.21 简述使用@Autowired注解自动装配的过程"></a>1.21 简述使用@Autowired注解自动装配的过程</h2></li>
</ul>
</li>
<li>答：使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置，<context:annotation-config />。</li>
<li>在启动Spring IoC时，容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器，当容器扫描到@Autowied、@Resource或@Inject时，就会在IoC容器自动查找需要的bean，并装配给该对象的属性。在使用@Autowired时，首先在容器中查询对应类型的bean：</li>
<li>如果查询结果刚好为一个，就将该bean装配给@Autowired指定的数据；</li>
<li>如果查询的结果不止一个，那么@Autowired会根据名称来查找；</li>
<li>如果上述查找的结果为空，那么会抛出异常。解决方法时，使用required=false。<h2 id="1-22-简述自动装配有哪些局限性"><a href="#1-22-简述自动装配有哪些局限性" class="headerlink" title="1.22 简述自动装配有哪些局限性"></a>1.22 简述自动装配有哪些局限性</h2></li>
<li>自动装配的局限性是：<ul>
<li>重写：你仍需用和配置来定义依赖，意味着总要重写自动装配。</li>
<li>基本数据类型：你不能自动装配简单的属性，如基本数据类型，String字符串和类。</li>
<li>模糊特性：自动装配不如显式装配精确，如果有可能，建议使用显式装配。<h2 id="1-23-可以在Spring中注入一个null-和一个空字符串"><a href="#1-23-可以在Spring中注入一个null-和一个空字符串" class="headerlink" title="1.23 可以在Spring中注入一个null 和一个空字符串"></a>1.23 可以在Spring中注入一个null 和一个空字符串</h2></li>
</ul>
</li>
<li>可以。</li>
<li>@Component, @Controller, @Repository, @Service 有何区别？<ul>
<li>@Component：这将 java 类标记为 bean。它是任何 Spring管理组件的通用构造型。Spring的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。</li>
<li>@Controller：这将一个类标记为 SpringWeb MVC 控制器。标有它的 Bean 会自动导入到 IoC 容器中。</li>
<li>@Service：此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component，因为它以更好的方式指定了意图。</li>
<li>@Repository：这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器，并使未经检查的异常有资格转换为 SpringDataAccessException。<h2 id="1-24-简述-Required-注解作用"><a href="#1-24-简述-Required-注解作用" class="headerlink" title="1.24 简述@Required 注解作用"></a>1.24 简述@Required 注解作用</h2></li>
</ul>
</li>
<li>这个注解表明bean的属性必须在配置的时候设置，通过一个bean定义的显式的属性值或通过自动装配，若@Required注解的bean属性未被设置，容器将抛出BeanInitializationException。<h2 id="1-25-简述-Autowired注解作用"><a href="#1-25-简述-Autowired注解作用" class="headerlink" title="1.25 简述@Autowired注解作用"></a>1.25 简述@Autowired注解作用</h2></li>
<li>@Autowired默认是按照类型装配注入的，默认情况下它要求依赖对象必须存在（可以设置它required属性为false）。@Autowired 注解提供了更细粒度的控制，包括在何处以及如何完成自动装配。它的用法和@Required一样，修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。<h2 id="1-26-简述-Autowired和-Resource的区别"><a href="#1-26-简述-Autowired和-Resource的区别" class="headerlink" title="1.26 简述@Autowired和@Resource的区别"></a>1.26 简述@Autowired和@Resource的区别</h2></li>
<li>@Autowired可用于：构造函数、成员变量、Setter方法</li>
<li>@Autowired和@Resource之间的区别<ul>
<li>@Autowired默认是按照类型装配注入的，默认情况下它要求依赖对象必须存在（可以设置它required属性为false）。</li>
<li>@Resource默认是按照名称来装配注入的，只有当找不到与名称匹配的bean才会按照类型来装配注入<h2 id="1-27-简述-Qualifier-注解作用"><a href="#1-27-简述-Qualifier-注解作用" class="headerlink" title="1.27 简述@Qualifier 注解作用"></a>1.27 简述@Qualifier 注解作用</h2></li>
</ul>
</li>
<li>创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时，您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。<h2 id="1-28-简述-RequestMapping-注解作用"><a href="#1-28-简述-RequestMapping-注解作用" class="headerlink" title="1.28 简述@RequestMapping 注解作用"></a>1.28 简述@RequestMapping 注解作用</h2></li>
<li>@RequestMapping 注解用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别：<ul>
<li>类级别：映射请求的 URL</li>
<li>方法级别：映射 URL 以及 HTTP 请求方法<h2 id="1-29-简述Spring支持Spring事务实现方式"><a href="#1-29-简述Spring支持Spring事务实现方式" class="headerlink" title="1.29 简述Spring支持Spring事务实现方式"></a>1.29 简述Spring支持Spring事务实现方式</h2></li>
</ul>
</li>
<li>编程式事务管理：这意味你通过编程的方式管理事务，给你带来极大的灵活性，但是难维护。</li>
<li>声明式事务管理：这意味着你可以将业务代码和事务管理分离，你只需用注解和XML配置来管理事务。<h2 id="1-30-简述Spring事务的实现方式及原理"><a href="#1-30-简述Spring事务的实现方式及原理" class="headerlink" title="1.30 简述Spring事务的实现方式及原理"></a>1.30 简述Spring事务的实现方式及原理</h2></li>
<li>Spring事务的本质其实就是数据库对事务的支持，没有数据库的事务支持，Spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。<h2 id="1-31-简述Spring的事务传播行为"><a href="#1-31-简述Spring的事务传播行为" class="headerlink" title="1.31 简述Spring的事务传播行为"></a>1.31 简述Spring的事务传播行为</h2></li>
<li>spring事务的传播行为说的是，当多个事务同时存在的时候，spring如何处理这些事务的行为。<ul>
<li>① PROPAGATION_REQUIRED：如果当前没有事务，就创建一个新事务，如果当前存在事务，就加入该事务，该设置是最常用的设置。</li>
<li>② PROPAGATION_SUPPORTS：支持当前事务，如果当前存在事务，就加入该事务，如果当前不存在事务，就以非事务执行。</li>
<li>③ PROPAGATION_MANDATORY：支持当前事务，如果当前存在事务，就加入该事务，如果当前不存在事务，就抛出异常。</li>
<li>④ PROPAGATION_REQUIRES_NEW：创建新事务，无论当前存不存在事务，都创建新事务。</li>
<li>⑤ PROPAGATION_NOT_SUPPORTED：以非事务方式执行操作，如果当前存在事务，就把当前事务挂起。</li>
<li>⑥ PROPAGATION_NEVER：以非事务方式执行，如果当前存在事务，则抛出异常。</li>
<li>⑦ PROPAGATION_NESTED：如果当前存在事务，则在嵌套事务内执行。如果当前没有事务，则按REQUIRED属性执行。<h2 id="1-32-简述Spring的事务隔离"><a href="#1-32-简述Spring的事务隔离" class="headerlink" title="1.32 简述Spring的事务隔离"></a>1.32 简述Spring的事务隔离</h2></li>
</ul>
</li>
<li>Spring有五大隔离级别，默认值为 ISOLATION_DEFAULT（使用数据库的设置），其他四个隔离级别和数据库的隔离级别一致：<ul>
<li>1.ISOLATION_DEFAULT：用底层数据库的设置隔离级别，数据库设置的是什么我就用什么；</li>
<li>2.ISOLATION_READ_UNCOMMITTED：未提交读，最低隔离级别、事务未提交前，就可被其他事务读取（会出现幻读、脏读、不可重复读）；</li>
<li>3.ISOLATION_READ_COMMITTED：提交读，一个事务提交后才能被其他事务读取到（会造成幻读、不可重复读），SQL server 的默认级别；</li>
<li>4.ISOLATION_REPEATABLE_READ：可重复读，保证多次读取同一个数据时，其值都和事务开始时候的内容是一致，禁止读取到别的事务未提交的数据（会造成幻读），MySQL 的默认级别；</li>
<li>5.ISOLATION_SERIALIZABLE：序列化，代价最高最可靠的隔离级别，该隔离级别能防止脏读、不可重复读、幻读。<ul>
<li>脏读 ：表示一个事务能够读取另一个事务中还未提交的数据。比如，某个事务尝试插入记录 A，此时该事务还未提交，然后另一个事务尝试读取到了记录 A。</li>
<li>不可重复读 ：是指在一个事务内，多次读同一数据。</li>
<li>幻读 ：指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录，但是第二次同等条件下查询却有 n+1 条记录，这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据，同一个记录的数据内容被修改了，所有数据行的记录就变多或者变少了。<h2 id="1-33-简述Spring框架的事务管理优点"><a href="#1-33-简述Spring框架的事务管理优点" class="headerlink" title="1.33 简述Spring框架的事务管理优点"></a>1.33 简述Spring框架的事务管理优点</h2></li>
</ul>
</li>
</ul>
</li>
<li>为不同的事务API 如 JTA，JDBC，Hibernate，JPA 和JDO，提供一个不变的编程模式。</li>
<li>为编程式事务管理提供了一套简单的API而不是一些复杂的事务API</li>
<li>支持声明式事务管理。</li>
<li>和Spring各种数据访问抽象层很好得集成。<h2 id="1-34-简述事务管理类型"><a href="#1-34-简述事务管理类型" class="headerlink" title="1.34 简述事务管理类型"></a>1.34 简述事务管理类型</h2></li>
<li>答：大多数Spring框架的用户选择声明式事务管理，因为它对应用代码的影响最小，因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理，虽然比编程式事务管理（这种方式允许你通过代码控制事务）少了一点灵活性。唯一不足地方是，最细粒度只能作用到方法级别，无法做到像编程式事务那样可以作用到代码块级别。<h2 id="1-35-简述AOP"><a href="#1-35-简述AOP" class="headerlink" title="1.35 简述AOP"></a>1.35 简述AOP</h2></li>
<li>OOP(Object-Oriented Programming)面向对象编程，允许开发者定义纵向的关系，但并适用于定义横向的关系，导致了大量代码的重复，而不利于各个模块的重用。</li>
<li>AOP(Aspect-Oriented Programming)，一般称为面向切面编程，作为面向对象的一种补充，用于将那些与业务无关，但却对多个对象产生影响的公共行为和逻辑，抽取并封装为一个可重用的模块，这个模块被命名为“切面”（Aspect），减少系统中的重复代码，降低了模块间的耦合度，同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。<h2 id="1-36-简述Spring-AOP和AspectJ-AOP"><a href="#1-36-简述Spring-AOP和AspectJ-AOP" class="headerlink" title="1.36 简述Spring AOP和AspectJ AOP"></a>1.36 简述Spring AOP和AspectJ AOP</h2></li>
<li>AOP实现的关键<ul>
<li>代理模式</li>
</ul>
</li>
<li>AOP代理主要分为<ul>
<li>静态代理<ul>
<li>静态代理的代表为AspectJ<ul>
<li>（1）AspectJ是静态代理的增强，所谓静态代理，就是AOP框架会在编译阶段生成AOP代理类，因此也称为编译时增强，他会在编译阶段将AspectJ(切面)织入到Java字节码中，运行的时候就是增强之后的AOP对象。</li>
</ul>
</li>
</ul>
</li>
<li>动态代理<ul>
<li>动态代理代表Spring AOP<ul>
<li>（2）Spring AOP使用的动态代理，所谓的动态代理就是说AOP框架不会去修改字节码，而是每次运行时在内存中临时为方法生成一个AOP对象，这个AOP对象包含了目标对象的全部方法，并且在特定的切点做了增强处理，并回调原对象的方法。<h2 id="1-37-简述JDK动态代理和CGLIB动态代理的区别"><a href="#1-37-简述JDK动态代理和CGLIB动态代理的区别" class="headerlink" title="1.37 简述JDK动态代理和CGLIB动态代理的区别"></a>1.37 简述JDK动态代理和CGLIB动态代理的区别</h2></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Spring AOP中的动态代理主要有两种方式，JDK动态代理和CGLIB动态代理：<ul>
<li>JDK动态代理只提供接口的代理，不支持类的代理。核心InvocationHandler接口和Proxy类，InvocationHandler 通过invoke()方法反射来调用目标类中的代码，动态地将横切逻辑和业务编织在一起；接着，Proxy利用 InvocationHandler动态创建一个符合某一接口的实例, 生成目标类的代理对象。<ul>
<li>如果代理类没有实现 InvocationHandler 接口，那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB（Code Generation Library），是一个代码生成的类库，可以在运行时动态的生成指定类的一个子类对象，并覆盖其中特定方法并添加增强代码，从而实现AOP。CGLIB是通过继承的方式做的动态代理，因此如果某个类被标记为final，那么它是无法使用CGLIB做动态代理的。</li>
</ul>
</li>
<li>静态代理与动态代理区别在于生成AOP代理对象的时机不同，相对来说AspectJ的静态代理方式具有更好的性能，但是AspectJ需要特定的编译器进行处理，而SpringAOP则无需特定的编译器处理。</li>
<li>InvocationHandler 的 invoke(Object proxy,Method method,Object[] args)：proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。<h2 id="1-38-简述Spring中的代理"><a href="#1-38-简述Spring中的代理" class="headerlink" title="1.38 简述Spring中的代理"></a>1.38 简述Spring中的代理</h2></li>
</ul>
</li>
<li>答：将 Advice 应用于目标对象后创建的对象称为代理。在客户端对象的情况下，目标对象和代理对象是相同的。<h2 id="1-39-简述Spring-AOP名词"><a href="#1-39-简述Spring-AOP名词" class="headerlink" title="1.39 简述Spring AOP名词"></a>1.39 简述Spring AOP名词</h2></li>
<li>1）切面（Aspect）：切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在SpringAOP中，切面可以使用通用类（基于模式的风格） 或者在普通类中以 @Aspect 注解来实现。</li>
<li>2）连接点（Join point）：指方法，在SpringAOP中，一个连接点 总是 代表一个方法的执行。 应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中，并添加新的行为。</li>
<li>3）通知（Advice）：在AOP术语中，切面的工作被称为通知。</li>
<li>4）切入点（Pointcut）：切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称，或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。</li>
<li>5）引入（Introduction）：引入允许我们向现有类添加新方法或属性。</li>
<li>6）目标对象（Target Object）： 被一个或者多个切面（aspect）所通知（advise）的对象。它通常是一个代理对象。也有人把它叫做 被通知（adviced） 对象。 既然SpringAOP是通过运行时代理实现的，这个对象永远是一个 被代理（proxied） 对象。</li>
<li>7）织入（Weaving）：织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入：<ul>
<li>编译期：切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。</li>
<li>类加载期：切面在目标类加载到JVM时被织入。需要特殊的类加载器，它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。</li>
<li>运行期：切面在应用运行的某个时刻被织入。一般情况下，在织入切面时，AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。<h2 id="1-40-简述Spring在运行时通知对象"><a href="#1-40-简述Spring在运行时通知对象" class="headerlink" title="1.40 简述Spring在运行时通知对象"></a>1.40 简述Spring在运行时通知对象</h2></li>
</ul>
</li>
<li>通过在代理类中包裹切面，Spring在运行期把切面织入到Spring管理的bean中。代理封装了目标类，并拦截被通知方法的调用，再把调用转发给真正的目标bean。当代理拦截到方法调用时，在调用目标bean方法之前，会执行切面逻辑。<h2 id="1-41-简述Spring只支持方法级别的连接点"><a href="#1-41-简述Spring只支持方法级别的连接点" class="headerlink" title="1.41 简述Spring只支持方法级别的连接点"></a>1.41 简述Spring只支持方法级别的连接点</h2></li>
<li>直到应用需要被代理的bean时，Spring才创建代理对象。如果使用的是ApplicationContext的话，在ApplicationContext从BeanFactory中加载所有bean的时候，Spring才会创建被代理的对象。因为Spring运行时才创建代理对象，所以我们不需要特殊的编译器来织入SpringAOP的切面。</li>
<li>因为Spring基于动态代理，所以Spring只支持方法连接点。Spring缺少对字段连接点的支持，而且它不支持构造器连接点。方法之外的连接点拦截功能，我们可以利用Aspect来补充。<h2 id="1-42-简述SpringAOP关注点和横切关注点的区别"><a href="#1-42-简述SpringAOP关注点和横切关注点的区别" class="headerlink" title="1.42 简述SpringAOP关注点和横切关注点的区别"></a>1.42 简述SpringAOP关注点和横切关注点的区别</h2></li>
<li>关注点（concern）是应用中一个模块的行为，一个关注点可能会被定义成一个想实现的一个功能。</li>
<li>横切关注点（cross-cutting concern）是一个关注点，此关注点是整个应用都会使用的功能，并影响整个应用，比如日志，安全等，几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。<h2 id="1-43-Spring通知有哪些类型？"><a href="#1-43-Spring通知有哪些类型？" class="headerlink" title="1.43 Spring通知有哪些类型？"></a>1.43 Spring通知有哪些类型？</h2></li>
<li>在AOP术语中，切面的工作被称为通知，实际上是程序执行时要通过SpringAOP框架触发的代码段。</li>
<li>Spring切面可以应用5种类型的通知：<ul>
<li><ol>
<li>前置通知（Before）：在目标方法被调用之前调用通知功能；</li>
</ol>
</li>
<li>2.后置通知（After）：在目标方法完成之后调用通知，此时不会关心方法的输出是什么；</li>
<li>3.返回通知（After-returning ）：在目标方法成功执行之后调用通知；</li>
<li>4.异常通知（After-throwing）：在目标方法抛出异常后调用通知；</li>
<li>5.环绕通知（Around）：通知包裹了被通知的方法，在被通知的方法调用之前和调用之后执行自定义的行为。<ul>
<li>同一个aspect，不同advice的执行顺序：<ul>
<li>①没有异常情况下的执行顺序：<br>around before advice<br>before advice<br>target method 执行<br>around after advice<br>after advice<br>afterReturning</li>
<li>②有异常情况下的执行顺序：<br>around before advice<br>before advice<br>target method 执行<br>around after advice<br>after advice<br>afterThrowing:异常发生<br>java.lang.RuntimeException: 异常发生<h2 id="1-44-简述切面Aspect"><a href="#1-44-简述切面Aspect" class="headerlink" title="1.44 简述切面Aspect"></a>1.44 简述切面Aspect</h2></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>aspect 由 pointcount 和 advice 组成，切面是通知和切点的结合。 它既包含了横切逻辑的定义, 也包括了连接点的定义. SpringAOP 就是负责实施切面的框架, 它将切面所定义的横切逻辑编织到切面所指定的连接点中.</li>
<li>AOP 的工作重心在于如何将增强编织目标对象的连接点上, 这里包含两个工作:<ul>
<li>如何通过 pointcut 和 advice 定位到特定的 joinpoint 上</li>
<li>如何在 advice 中编写切面代码.</li>
</ul>
</li>
<li>可以简单地认为, 使用 @Aspect 注解的类就是切面.<h2 id="1-45-简述Spring-MVC"><a href="#1-45-简述Spring-MVC" class="headerlink" title="1.45 简述Spring MVC"></a>1.45 简述Spring MVC</h2></li>
<li>SpringMVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架，通过把模型-视图-控制器分离，将web层进行职责解耦，把复杂的web应用分成逻辑清晰的几部分，简化开发，减少出错，方便组内开发人员之间的配合。<h2 id="1-46-简述Spring-MVC的优点"><a href="#1-46-简述Spring-MVC的优点" class="headerlink" title="1.46 简述Spring MVC的优点"></a>1.46 简述Spring MVC的优点</h2></li>
<li>（1）可以支持各种视图技术,而不仅仅局限于JSP；</li>
<li>（2）与Spring框架集成（如IoC容器、AOP等）；</li>
<li>（3）清晰的角色分配：前端控制器(dispatcherServlet) , 请求到处理器映射（handlerMapping), 处理器适配器（HandlerAdapter), 视图解析器（ViewResolver）。</li>
<li>（4） 支持各种请求资源的映射策略。<h2 id="1-47-简述Spring-MVC的主要组件"><a href="#1-47-简述Spring-MVC的主要组件" class="headerlink" title="1.47 简述Spring MVC的主要组件"></a>1.47 简述Spring MVC的主要组件</h2></li>
<li>（1）前端控制器 DispatcherServlet（不需要程序员开发）<ul>
<li>作用：接收请求、响应结果，相当于转发器，有了DispatcherServlet 就减少了其它组件之间的耦合度。</li>
</ul>
</li>
<li>（2）处理器映射器HandlerMapping（不需要程序员开发）<ul>
<li>作用：根据请求的URL来查找Handler</li>
</ul>
</li>
<li>（3）处理器适配器HandlerAdapter<ul>
<li>注意：在编写Handler的时候要按照HandlerAdapter要求的规则去编写，这样适配器HandlerAdapter才可以正确的去执行Handler。</li>
</ul>
</li>
<li>（4）处理器Handler（需要程序员开发）</li>
<li>（5）视图解析器 ViewResolver（不需要程序员开发）<ul>
<li>作用：进行视图的解析，根据视图逻辑名解析成真正的视图（view）</li>
</ul>
</li>
<li>（6）视图View（需要程序员开发jsp）<ul>
<li>View是一个接口， 它的实现类支持不同的视图类型（jsp，freemarker，pdf等等）<h2 id="1-48-简述DispatcherServlet"><a href="#1-48-简述DispatcherServlet" class="headerlink" title="1.48 简述DispatcherServlet"></a>1.48 简述DispatcherServlet</h2></li>
</ul>
</li>
<li>Spring的MVC框架是围绕DispatcherServlet来设计的，它用来处理所有的HTTP请求和响应。<h2 id="1-49-简述是Spring-MVC框架的控制器"><a href="#1-49-简述是Spring-MVC框架的控制器" class="headerlink" title="1.49 简述是Spring MVC框架的控制器"></a>1.49 简述是Spring MVC框架的控制器</h2></li>
<li>控制器提供一个访问应用程序的行为，此行为通常通过服务接口实现。</li>
<li>控制器解析用户输入并将其转换为一个由视图呈现给用户的模型。</li>
<li>Spring用一个非常抽象的方式实现了一个控制层，允许用户创建多种用途的控制器。<h2 id="1-50-简述Spring-MVC的控制器"><a href="#1-50-简述Spring-MVC的控制器" class="headerlink" title="1.50 简述Spring MVC的控制器"></a>1.50 简述Spring MVC的控制器</h2></li>
<li>控制器是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的。<h2 id="1-51-简述Spring-MVC的工作流程"><a href="#1-51-简述Spring-MVC的工作流程" class="headerlink" title="1.51 简述Spring MVC的工作流程"></a>1.51 简述Spring MVC的工作流程</h2></li>
<li>（1）用户发送请求至前端控制器DispatcherServlet；</li>
<li>（2） DispatcherServlet收到请求后，调用HandlerMapping处理器映射器，请求获取Handle；</li>
<li>（3）处理器映射器根据请求url找到具体的处理器，生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet；</li>
<li>（4）DispatcherServlet 调用 HandlerAdapter处理器适配器；</li>
<li>（5）HandlerAdapter 经过适配调用 具体处理器(Handler，也叫后端控制器)；</li>
<li>（6）Handler执行完成返回ModelAndView；</li>
<li>（7）HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet；</li>
<li>（8）DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析；</li>
<li>（9）ViewResolver解析后返回具体View；</li>
<li>（10）DispatcherServlet对View进行渲染视图（即将模型数据填充至视图中）</li>
<li>（11）DispatcherServlet响应用户。<h2 id="1-52-简述Spring-MVC"><a href="#1-52-简述Spring-MVC" class="headerlink" title="1.52 简述Spring MVC"></a>1.52 简述Spring MVC</h2></li>
<li>mvc是一种设计模式（设计模式就是日常开发中编写代码的一种好的方法和经验的总结）。<ul>
<li>三层架构的设计模式:模型（model）-视图（view）-控制器（controller）用于实现前端页面的展现与后端业务数据处理的分离。</li>
</ul>
</li>
<li>mvc设计模式的好处：<ul>
<li>1.分层设计，实现了业务系统各个组件之间的解耦，有利于业务系统的可扩展性，可维护性。</li>
<li>2.有利于系统的并行开发，提升开发效率。<h2 id="1-53-简述注解原理"><a href="#1-53-简述注解原理" class="headerlink" title="1.53 简述注解原理"></a>1.53 简述注解原理</h2></li>
</ul>
</li>
<li>注解本质是一个继承了Annotation的特殊接口，其具体实现类是Java运行时生成的动态代理类。我们通过反射获取注解时，返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法，会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。<h2 id="1-54-简述Spring-MVC常用的注解有哪些？"><a href="#1-54-简述Spring-MVC常用的注解有哪些？" class="headerlink" title="1.54 简述Spring MVC常用的注解有哪些？"></a>1.54 简述Spring MVC常用的注解有哪些？</h2></li>
<li>@RequestMapping：用于处理请求 url 映射的注解，可用于类或方法上。用于类上，则表示类中的所有响应请求的方法都是以该地址作为父路径。</li>
<li>@RequestBody：注解实现接收http请求的json数据，将json转换为java对象。</li>
<li>@ResponseBody：注解实现将conreoller方法返回对象转化为json对象响应给客户。<h2 id="1-55-简述Sping-MVC中的控制器的注解"><a href="#1-55-简述Sping-MVC中的控制器的注解" class="headerlink" title="1.55 简述Sping MVC中的控制器的注解"></a>1.55 简述Sping MVC中的控制器的注解</h2></li>
<li>一般用@Controller注解,也可以使用@RestController,@RestController注解相当于@ResponseBody＋@Controller,表示是表现层,除此之外，一般不用别的注解代替。<h2 id="1-56-简述-Controller注解的作用"><a href="#1-56-简述-Controller注解的作用" class="headerlink" title="1.56 简述@Controller注解的作用"></a>1.56 简述@Controller注解的作用</h2></li>
<li>在SpringMVC 中，控制器Controller 负责处理由DispatcherServlet 分发的请求，它把用户请求的数据经过业务处理层处理之后封装成一个Model ，然后再把该Model 返回给对应的View 进行展示。在SpringMVC 中提供了一个非常简便的定义Controller 的方法，你无需继承特定的类或实现特定的接口，只需使用@Controller 标记一个类是Controller ，然后使用@RequestMapping 和@RequestParam 等一些注解用以定义URL 请求和Controller 方法之间的映射，这样的Controller 就能被外界访问到。此外Controller 不会直接依赖于HttpServletRequest 和HttpServletResponse 等HttpServlet 对象，它们可以通过Controller 的方法参数灵活的获取到。</li>
<li>@Controller 用于标记在一个类上，使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法，并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类，而使用@RequestMapping 注解的方法才是真正处理请求的处理器。单单使用@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类，因为这个时候Spring还不认识它。那么要如何做Spring才能认识它呢？这个时候就需要我们把这个控制器类交给Spring来管理。有两种方式：</li>
<li>在SpringMVC 的配置文件中定义MyController 的bean 对象。</li>
<li>在SpringMVC 的配置文件中告诉Spring该到哪里去找标记为@Controller的Controller 控制器。<h2 id="1-57-简述-RequestMapping注解的作用"><a href="#1-57-简述-RequestMapping注解的作用" class="headerlink" title="1.57 简述@RequestMapping注解的作用"></a>1.57 简述@RequestMapping注解的作用</h2></li>
<li>RequestMapping是一个用来处理请求地址映射的注解，可用于类或方法上。用于类上，表示类中的所有响应请求的方法都是以该地址作为父路径。</li>
<li>RequestMapping注解有六个属性，下面我们把她分成三类进行说明（下面有相应示例）。<ul>
<li>value， method<ul>
<li>value： 指定请求的实际地址，指定的地址可以是URI Template 模式（后面将会说明）；</li>
<li>method： 指定请求的method类型， GET、POST、PUT、DELETE等；</li>
</ul>
</li>
<li>consumes，produces<ul>
<li>consumes： 指定处理请求的提交内容类型（Content-Type），例如application/json, text/html;</li>
<li>produces: 指定返回的内容类型，仅当request请求头中的(Accept)类型中包含该指定类型才返回；</li>
</ul>
</li>
<li>params，headers<ul>
<li>params： 指定request中必须包含某些参数值是，才让该方法处理。</li>
<li>headers： 指定request中必须包含某些指定的header值，才能让该方法处理请求。<h2 id="1-58-简述-ResponseBody注解的作用"><a href="#1-58-简述-ResponseBody注解的作用" class="headerlink" title="1.58 简述@ResponseBody注解的作用"></a>1.58 简述@ResponseBody注解的作用</h2></li>
</ul>
</li>
</ul>
</li>
<li>该注解用于将Controller的方法返回的对象，通过适当的HttpMessageConverter转换为指定格式后，写入到Response对象的body数据区。使用时机：返回的数据不是html标签的页面，而是其他某种格式的数据时（如json、xml等）使用；<h2 id="1-59-简述-PathVariable和-RequestParam的区别"><a href="#1-59-简述-PathVariable和-RequestParam的区别" class="headerlink" title="1.59 简述@PathVariable和@RequestParam的区别"></a>1.59 简述@PathVariable和@RequestParam的区别</h2></li>
<li>请求路径上有个id的变量值，可以通过@PathVariable来获取 @RequestMapping(value = “/page/{id}”, method = RequestMethod.GET)；@RequestParam用来获得静态的URL请求入参 spring注解时action里用到。<h2 id="1-60-简述Spring-MVC与Struts2区别"><a href="#1-60-简述Spring-MVC与Struts2区别" class="headerlink" title="1.60 简述Spring MVC与Struts2区别"></a>1.60 简述Spring MVC与Struts2区别</h2></li>
<li>相同点：<ul>
<li>都是基于mvc的表现层框架，都用于web项目的开发。</li>
</ul>
</li>
<li>不同点：<ul>
<li>1.前端控制器不一样。Spring MVC的前端控制器是Servlet：DispatcherServlet。struts2的前端控制器是filter：StrutsPreparedAndExcutorFilter。</li>
<li>2.请求参数的接收方式不一样。Spring MVC是使用方法的形参接收请求的参数，基于方法的开发，线程安全，可以设计为单例或者多例的开发，推荐使用单例模式的开发（执行效率更高），默认就是单例开发模式。struts2是通过类的成员变量接收请求的参数，是基于类的开发，线程不安全，只能设计为多例的开发。</li>
<li>3.Struts采用值栈存储请求和响应的数据，通过OGNL存取数据，Spring MVC通过参数解析器是将request请求内容解析，并给方法形参赋值，将数据和视图封装成ModelAndView对象，最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。</li>
<li>4.与Spring整合不一样。Spring MVC是Spring框架的一部分，不需要整合。在企业项目中，Spring MVC使用更多一些。<h2 id="1-61-简述SpringMVC设定重定向和转发"><a href="#1-61-简述SpringMVC设定重定向和转发" class="headerlink" title="1.61 简述SpringMVC设定重定向和转发"></a>1.61 简述SpringMVC设定重定向和转发</h2></li>
</ul>
</li>
<li>（1）转发：在返回值前面加”forward:”，譬如”forward:index “</li>
<li>（2）重定向：在返回值前面加”redirect:”，譬如”redirect:<a target="_blank" rel="noopener" href="http://www.xunhe.com">http://www.xunhe.com</a>“<ul>
<li>重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置<h2 id="1-62-Spring-MVC怎么和AJAX相互调用的"><a href="#1-62-Spring-MVC怎么和AJAX相互调用的" class="headerlink" title="1.62 Spring MVC怎么和AJAX相互调用的"></a>1.62 Spring MVC怎么和AJAX相互调用的</h2></li>
</ul>
</li>
<li>通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 ：<ul>
<li>（1）加入Jackson.jar</li>
<li>（2）在配置文件中配置json的映射</li>
<li>（3）在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。<h2 id="1-63-如何解决POST请求中文乱码问题"><a href="#1-63-如何解决POST请求中文乱码问题" class="headerlink" title="1.63 如何解决POST请求中文乱码问题"></a>1.63 如何解决POST请求中文乱码问题</h2></li>
</ul>
</li>
<li>解决post请求乱码问题：<ul>
<li>在web.xml中配置一个CharacterEncodingFilter过滤器，设置成utf-8<h2 id="1-64-简述SpringMVC的异常处理"><a href="#1-64-简述SpringMVC的异常处理" class="headerlink" title="1.64 简述SpringMVC的异常处理"></a>1.64 简述SpringMVC的异常处理</h2></li>
</ul>
</li>
<li>可以将异常抛给Spring框架，由Spring框架来处理；只需要配置简单的异常处理器，在异常处理器中添视图页面即可<h2 id="1-65-简述如何拦截Get方式提交的方法"><a href="#1-65-简述如何拦截Get方式提交的方法" class="headerlink" title="1.65 简述如何拦截Get方式提交的方法"></a>1.65 简述如何拦截Get方式提交的方法</h2></li>
<li>可以在@RequestMapping注解里面加上method=RequestMethod.GET<h2 id="1-66-简述在方法里面如何得到Request"><a href="#1-66-简述在方法里面如何得到Request" class="headerlink" title="1.66 简述在方法里面如何得到Request"></a>1.66 简述在方法里面如何得到Request</h2></li>
<li>直接在方法的形参中声明request,Spring MVC就自动把request对象传入<h2 id="1-67-简述Spring-MVC中函数的返回值"><a href="#1-67-简述Spring-MVC中函数的返回值" class="headerlink" title="1.67 简述Spring MVC中函数的返回值"></a>1.67 简述Spring MVC中函数的返回值</h2></li>
<li>返回值可以有很多类型有：String, ModelAndView。ModelAndView类把视图和数据都合并的一起的，但一般用String比较好<h2 id="1-68-Spring-MVC用何对象从后台向前台传递数据"><a href="#1-68-Spring-MVC用何对象从后台向前台传递数据" class="headerlink" title="1.68 Spring MVC用何对象从后台向前台传递数据"></a>1.68 Spring MVC用何对象从后台向前台传递数据</h2></li>
<li>通过ModelMap对象,可以在这个对象里面调用put方法,把对象加到里面,前台就可以通过el表达式拿到<h2 id="1-69-如何实现Spring-MVC拦截器"><a href="#1-69-如何实现Spring-MVC拦截器" class="headerlink" title="1.69 如何实现Spring MVC拦截器"></a>1.69 如何实现Spring MVC拦截器</h2></li>
<li>一种是实现HandlerInterceptor接口</li>
<li>另外一种是继承适配器类，接着在接口方法当中，实现处理逻辑；然后在Spring MVC的配置文件中配置拦截器即可：</li>
<li>WebApplicationContext 继承了ApplicationContext 并增加了一些WEB应用必备的特有功能，它不同于一般的ApplicationContext ，因为它能处理主题，并找到被关联的Servlet。<h2 id="1-70-简述-Transactional失效场景"><a href="#1-70-简述-Transactional失效场景" class="headerlink" title="1.70 简述@Transactional失效场景"></a>1.70 简述@Transactional失效场景</h2></li>
<li>1.@Transactional注解标注方法修饰符为非public时，@Transactional注解将会不起作用。</li>
<li>2.在类内部调用类内部@Transactional标注的方法。这种情况下也会导致事务不开启；</li>
<li>3.事务方法内部捕捉了异常，没有抛出新的异常，导致事务操作不会进行回滚。</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/Spring%E5%BC%80%E5%8F%91/%E4%B8%80%E3%80%81spring%E5%9F%BA%E7%A1%80%E4%BB%8B%E7%BB%8D/" data-id="cl403sxqr000of8vufzvt7oph" data-title="一、spring基础介绍" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-vba基础操作笔记/vba基础操作" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/01/02/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C%E7%AC%94%E8%AE%B0/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C/" class="article-date">
  <time class="dt-published" datetime="2022-01-02T06:56:00.000Z" itemprop="datePublished">2022-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/notes-on-computer-expertise/categories/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C/">vba基础操作</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/notes-on-computer-expertise/2022/01/02/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C%E7%AC%94%E8%AE%B0/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C/">vba基础操作</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h2 id="vba基础数据结构"><a href="#vba基础数据结构" class="headerlink" title="vba基础数据结构"></a>vba基础数据结构</h2><h3 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h3><p>函数Len来计算这个字符串的长度</p>
<h3 id="VBA数组"><a href="#VBA数组" class="headerlink" title="VBA数组"></a>VBA数组</h3><p>声明数组<br>因为数组也是变量，所以，你必须用声明其它变量的类似方法声明数组——使用Dim语句。当你声明一个数组时，你便设定了该数组储存数据所需要的内存空间。我们来看看一个数组声明的例子：<br>Dim cities(6) As String<br>Dim daysOfWeek(7) As String<br>Dim lotto(6) As Integer<br>Dim exchange(5, 3) As Variant</p>
<p>注意，变量名称后面带有括号以及括号里有数字。一维数组要求括号里带一个数字，这个数字决定了这个数组能够储存的最大成员数。二维数组后面总是带有两个数字——第一个数字是行索引号，而第二个数字是列索引号。在上面的例子里，数组exchange最多可以储存15个数据(5<em>3=15)。<br>数组声明的最后一部份是定义数组将要储存数据的数据类型。数组可以储存下列任何一种数据类型：Integer, Long, Single, Double, Variant, Currency, String, Boolean, Byte, or Date。<br>当你声明了一个数组，VB会自动占据足够的内存空间，分配的内存空间取决于该数组的大小和数据类型。当你声明一个名叫lotto的带有6个成员的一维数组时，VB将留出12个字节——数组的每个成员各占2个字节（回想整型数据类型为2个字节，因此2</em>6=12）。数组越大，储存数据需要的内存空间就越大。因为数组会吃掉很多内存，并因此影响你电脑的运行，因此，建议你仅仅根据你可能使用的成员数来声明数组。<br>数组的上界和下界<br>VBA默认将数组的第一个成员设置为0（索引号），因此，数字1代表数组中的第二个成员，而数字2则代表第三个，等等。因为数字编号起始于0，所以，一维数组cities(6)包含从0到6的七个成员。如果你宁愿从1开始计数你数组里的成员，那么你可以使用Option Base 1语句来强制指定该数组的下界。该指令必须置于VBA模块任何Sub语句上面的声明部分。如果你不明确Option Base 1，那么VBA在使用数组是就会假定使用Option Base 0来从0开始编号你的数组成员。<br>你也可以让数组从除0或1之外的数字开始编号，要达到该目的，你在声明数组变量时就必须明确该数组的边界。数组的边界是指它最小和最大的索引号。我们来看看下面的例子：<br>Dim cities(3 To 6) As Integer<br>上面的语句声明了一个带有四个成员的一维数组。数组名称后面括号里的数字明确了数组的下界（3）和上界（6）。该数组的第一个成员编号为3，第二个为4，第三个为5，以及第四个为6。注意下界和上界之间的关键字To。<br>技巧：数组范围Dim语句明确的数组的下标区间就称为数组的范围，例如：Dim mktgCodes(5 To 15)<br>在VBA过程里使用数组你声明了数组后，就必须给该数组的每个成员赋值，这也经常成为“填充数组”。我们来尝试使用一维数组有规划地显示六个美国城市的清单：</p>
<ol>
<li>打开一个新工作簿，并保存为Chap07.xls2.  切换到VB编辑器窗口，并重新命名VBA工程为Tables3.插入一新模块，重新命名为StaticArrays4.输入下列过程FavoriteCities：<br>‘ start indexing array elements at 1 从1开始给数组成员编号<br>Option Base 1<br>Sub FavoriteCities()<br>‘now declare the array<br>Dim cities(6) As String<br>‘assign the values to array elements<br>cities(1) = “Baltimore”<br>cities(2) = “Atlanta”<br>cities(3) = “Boston”<br>cities(4) = “Washington”<br>cities(5) = “New York”<br>cities(6) = “Trenton”<br>‘display the list of cities<br>MsgBox cities(1) &amp; Chr(13) &amp; cities(2) &amp; Chr(13) _<br>&amp; cities(3) &amp; Chr(13) &amp; cities(4) &amp; Chr(13) _<br>&amp; cities (5) &amp; Chr(13) &amp; cities(6)<br>End Sub<br>在FavoriteCities过程开始之前，缺省的索引编号方式改变了，注意，Option Base 1语句是位于模块窗口Sub语句之上的。该语句告诉VB给数组的第一个成员赋值数字1，而不是缺省的0。<br>数组cities()声明为带六个成员的字符串类型变量。然后，给数组的每个成员都赋上了值。最后的语句使用Msgbox函数显示城市清单。当你运行该过程时，城市名称将会出现在分开的行上（参见图7-1）。你可以改变显示数据的顺序，改变索引号。<br>Msgbox<br>图7-1 你可以用Msgbox函数来显示一维数组的成员</li>
<li>运行FavoriteCities过程并且检查结果6.  修改FavoriteCities过程，让它逆序显示城市名称（从6到1）<br>技巧： 数组成员的初始值在给数组成员赋值之前，该成员具有缺省值。数字变量的缺省值为0，而字符串变量的缺省值为空字符串。</li>
</ol>
<p>VBA 获取数组的长度 原创<br>2018-05-11 22:20:18</p>
<p>少莫千华 码龄12年关注<br>REM “获取数组长度”Public Function ArrayLength(ByVal ary) As Integer    ArrayLength = UBound(ary) - LBound(ary) + 1End Function REM “调用示例”Sub SplitSample()    ‘声明    Dim temp() As String    Dim EgStr As String     ‘执行过程    EgStr = “str1   str2    str3”    temp = Split(EgStr, “ “)    MsgBox(ArrayLength(temp))</p>
<p>2、For循环的步长。</p>
<p>步长就是我们军训排队时，教官号令报数，单数出列。单数1、3、5、7…..之间都相差 2，这就是步长。</p>
<p>如，现在我们要在A1:A10单元格区域，依次选择单数A1、A3、A5……A9。步长是使用要在For语句后面写上</p>
<p>Step 步长值。</p>
<p>Dim i As Long</p>
<p>For i = 1 To 10 Step 2 ‘步长为2</p>
<p>Cells(i, 1).Select</p>
<p>Next</p>
<h2 id="vba基础操作"><a href="#vba基础操作" class="headerlink" title="vba基础操作"></a>vba基础操作</h2><h3 id="开启excel的vba模式"><a href="#开启excel的vba模式" class="headerlink" title="开启excel的vba模式"></a>开启excel的vba模式</h3><ul>
<li><p>启动完成软件，我们点击文件。</p>
<p><img src="@WRW7[S(HR%" alt="img">E8557RP9G1J.png)</p>
</li>
<li><p>然后我们点击下方的选项按钮。</p>
</li>
<li><p>接下来将会谈=弹出Excel选项窗口。</p>
</li>
<li><p>然后我们点击自定义功能区。</p>
</li>
<li><p>在右侧主选项卡中电勾开发工具。<br><img src="1 (2" alt="1 (2)">.png)</p>
</li>
<li><p>点击确定后即可成功开启。</p>
</li>
</ul>
<h3 id="基础语法介绍"><a href="#基础语法介绍" class="headerlink" title="基础语法介绍"></a>基础语法介绍</h3><h3 id="vba与文件处理"><a href="#vba与文件处理" class="headerlink" title="vba与文件处理"></a>vba与文件处理</h3><ul>
<li>后台处理其他文档的语句<ul>
<li>Application.ScreenUpdating=False<h4 id="VBA获取文件夹下所有文件夹或者文件名称"><a href="#VBA获取文件夹下所有文件夹或者文件名称" class="headerlink" title="VBA获取文件夹下所有文件夹或者文件名称"></a>VBA获取文件夹下所有文件夹或者文件名称</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">Sub childfile()</span><br><span class="line">parentpath = &quot;C:\Users\Administrator\Desktop\bbb\&quot;</span><br><span class="line">parentfile = Dir(mypath, vbDirectory)</span><br><span class="line">a = 1</span><br><span class="line">Do While parentfile &lt;&gt; &quot;&quot;</span><br><span class="line">If parentfile &lt;&gt; &quot;.&quot; And parentfile &lt;&gt; &quot;..&quot; Then</span><br><span class="line">Cells(a, 1) = parentfile</span><br><span class="line">a = a + 1</span><br><span class="line">parentfile = Dir</span><br><span class="line">Else</span><br><span class="line">parentfile = Dir</span><br><span class="line">End If</span><br><span class="line">Loop</span><br><span class="line">End Sub</span><br></pre></td></tr></table></figure>
<h4 id="vba操作excel"><a href="#vba操作excel" class="headerlink" title="vba操作excel"></a>vba操作excel</h4></li>
</ul>
</li>
<li>VBA获取文件夹下所有文件夹或者文件名称的方法<h4 id="vba操作word"><a href="#vba操作word" class="headerlink" title="vba操作word"></a>vba操作word</h4></li>
<li>打开word文档<ul>
<li>Documents.Open (“E:\nzork\文章\myword.docx”)</li>
</ul>
</li>
<li>获取当前文档的总页数<ul>
<li>Dim Doc As Document</li>
<li>Doc.Windows(1).Panes(1).Pages.Count</li>
</ul>
</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C%E7%AC%94%E8%AE%B0/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C/" data-id="cl403sxrl002cf8vu8knn6o93" data-title="vba基础操作" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-计算机网络/计算机网络基础知识" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/01/02/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/" class="article-date">
  <time class="dt-published" datetime="2022-01-02T06:56:00.000Z" itemprop="datePublished">2022-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/notes-on-computer-expertise/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/">计算机网络</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/notes-on-computer-expertise/2022/01/02/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/">计算机网络基础知识</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <ul>
<li><p>OSI七层模型</p>
<ul>
<li>物理层<ul>
<li>实际最终信号的传输是通过物理层实现的。通过物理介质传输比特流。规定了电平、速度和电缆针脚。常用设备有（各种物理设备）集线器、中继器、调制解调器、网线、双绞线、同轴电缆。这些都是物理层的传输介质。<ul>
<li>快递寄送过程中的交通工具，就相当于我们的物理层，例如汽车，火车，飞机，船</li>
</ul>
</li>
</ul>
</li>
<li>数据链路层<ul>
<li>将比特组合成字节,再将字节组合成帧,使用链路层地址 (以太网使用MAC地址)来访问介质,并进行差错检测<ul>
<li>媒体访问控制子层（MAC）<ul>
<li>LLC子层定义了一些字段使上次协议能共享数据链路层</li>
</ul>
</li>
<li>逻辑链路控制子层（LLC）<ul>
<li>MAC子层处理CSMA/CD算法、数据出错校验、成帧等</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>网络层</p>
<ul>
<li>本层通过IP寻址来建立两个节点之间的连接，为源端的运输层送来的分组，选择合适的路由和交换节点，正确无误地按照地址传送给目的端的运输层。就是通常说的IP层。这一层就是我们经常说的IP协议层。IP协议是Internet的基础</li>
<li>网络层就相当于快递公司庞大的快递网络，全国不同的集散中心，比如说，从深圳发往北京的顺丰快递（陆运为例啊，空运好像直接就飞到北京了），首先要到顺丰的深圳集散中心，从深圳集散中心再送到武汉集散中心，从武汉集散中心再寄到北京顺义集散中心。这个每个集散中心，就相当于网络中的一个IP节点。</li>
</ul>
</li>
<li><p>传输层</p>
<ul>
<li>传输层建立了主机端到端的链接，传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务，包括处理差错控制和流量控制等问题</li>
<li>传输层就相当于公司中的负责快递邮件收发的人，公司自己的投递员，他们负责将上一层的要寄出的资料投递到快递公司或邮局</li>
</ul>
</li>
<li><p>会话层</p>
<ul>
<li>会话层就是负责建立、管理和终止表示层实体之间的通信会话。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。    </li>
<li>会话层的同事拿到表示层的同事转换后资料，会话层的同事那里可能会掌握本公司与其他好多公司的联系方式，这里公司就是实际传递过程中的实体。他们要管理本公司与外界好多公司的联系会话。当接收到表示层的数据后，会话层将会建立并记录本次会话，他首先要找到公司B的地址信息，然后将整份资料放进信封，并写上地址和联系方式。准备将资料寄出。等到确定公司B接收到此份报价单后，此次会话就算结束了，外联部的同事就会终止此次会话<br>传输层+会话层=SSL</li>
</ul>
</li>
<li><p>表示层</p>
<ul>
<li>表示层提供各种用于应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别</li>
</ul>
</li>
<li>应用层<ul>
<li>OSI参考模型中最靠近用户的一层，是为计算机用户提供应用接口，也为用户直接提供各种网络服务</li>
<li>我们常见应用层的网络服务协议有：HTTP，HTTPS，FTP，POP3、SMTP<ul>
<li>发送的商业报价单</li>
<li>发一份商业合同</li>
<li>发一份询价单</li>
</ul>
</li>
</ul>
</li>
<li>通信特点<ul>
<li>对等通信      </li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li>TCP/IP五层模型</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/" data-id="cl403sxsm004rf8vues5508do" data-title="计算机网络基础知识" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-python/manim/manim基础整理" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/01/02/python/manim/manim%E5%9F%BA%E7%A1%80%E6%95%B4%E7%90%86/" class="article-date">
  <time class="dt-published" datetime="2022-01-02T06:56:00.000Z" itemprop="datePublished">2022-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/notes-on-computer-expertise/categories/manim/">manim</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/notes-on-computer-expertise/2022/01/02/python/manim/manim%E5%9F%BA%E7%A1%80%E6%95%B4%E7%90%86/">manim基础整理</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="manim"><a href="#manim" class="headerlink" title="manim"></a>manim</h1><h2 id="Installation"><a href="#Installation" class="headerlink" title="Installation"></a>Installation</h2><h3 id="Manim-runs-on-Python-3-6-or-higher-Python-3-8-is-recommended"><a href="#Manim-runs-on-Python-3-6-or-higher-Python-3-8-is-recommended" class="headerlink" title="Manim runs on Python 3.6 or higher (Python 3.8 is recommended)."></a>Manim runs on Python 3.6 or higher (Python 3.8 is recommended).</h3><h3 id="System-requirements-are"><a href="#System-requirements-are" class="headerlink" title="System requirements are"></a>System requirements are</h3><ul>
<li>FFmpeg</li>
<li>OpenGL (included in python package PyOpenGL)</li>
<li>LaTeX (optional, if you want to use LaTeX)</li>
<li>Pango (only for Linux)<h3 id="Directly"><a href="#Directly" class="headerlink" title="Directly"></a>Directly</h3></li>
<li>Install manimgl<ul>
<li>pip install manimgl</li>
</ul>
</li>
<li>Try it out<ul>
<li>manimgl</li>
</ul>
</li>
<li>If you want to hack on manimlib itself, clone this repository and in that directory execute:<ul>
<li>Install python requirements<ul>
<li>pip install -e .</li>
</ul>
</li>
<li>Try it out<ul>
<li>manimgl example_scenes.py OpeningManimExample# or</li>
<li>manim-render example_scenes.py OpeningManimExample</li>
</ul>
</li>
</ul>
</li>
<li>1.Install FFmpeg, and make sure that its path is in the PATH environment variable.</li>
<li>2.Install a LaTeX distribution. TeXLive-full is recommended.</li>
<li>3.Install the remaining Python packages.</li>
<li>git clone <a target="_blank" rel="noopener" href="https://github.com/3b1b/manim.gitcd">https://github.com/3b1b/manim.gitcd</a> manim</li>
<li>pip install -e .</li>
<li>manimgl example_scenes.py OpeningManimExample<h3 id="For-Anaconda"><a href="#For-Anaconda" class="headerlink" title="For Anaconda"></a>For Anaconda</h3></li>
<li>Install FFmpeg and LaTeX as above.</li>
<li>Create a conda environment using</li>
<li>git clone <a target="_blank" rel="noopener" href="https://github.com/3b1b/manim.gitcd">https://github.com/3b1b/manim.gitcd</a> manim</li>
<li>conda create -n manim python=3.8</li>
<li>conda activate manim</li>
<li>pip install -e .</li>
</ul>
<h2 id="python-贪吃蛇"><a href="#python-贪吃蛇" class="headerlink" title="python 贪吃蛇"></a>python 贪吃蛇</h2><h3 id="main主函数"><a href="#main主函数" class="headerlink" title="main主函数"></a>main主函数</h3><ul>
<li>相关初始化工作<ul>
<li>显示welcome</li>
<li>游戏结束了，显示game over</li>
</ul>
</li>
<li>游戏主题运行<ul>
<li>蛇身位置、食物位置初始化</li>
<li>监听键盘</li>
<li>判断蛇的状态，死亡</li>
<li>更新画面</li>
</ul>
</li>
<li>Screen=Square(color=YELLOW,fill_color=YELLOW,fill_opacity=0.2,width=8,height=8)</li>
<li>title=TextMobject(“python 贪吃蛇”)</li>
<li>title.to_edge(UP)</li>
<li>self.play(FadeIN(Screen),Write(title))</li>
<li>show_start_info(screen)               #欢迎信息</li>
<li>self.wait(3)</li>
<li>running_game(snake_speed=2)</li>
<li>show_gameover_info()<h3 id="代码框架"><a href="#代码框架" class="headerlink" title="代码框架"></a>代码框架</h3></li>
<li>def running_game(self,snake_speed):</li>
<li>开始后，进入游戏内循环，直到游戏结束</li>
<li>对于贪吃蛇，采用元组保存，根据元组打印出贪吃蛇</li>
<li>def draw_food(self,food):</li>
<li>def draw_snake(self,snake_coords):</li>
<li>def move_snake(self,snake_coords):</li>
<li>移动做法具体是，我们把每次头部移动的新坐标插入贪吃蛇元组，然后删掉尾部一节</li>
<li>def snake_is_alive(self,snake_coords):</li>
<li>def snake_is_eat(self,snake_coords,food):</li>
<li>def get_random_location(self):</li>
<li>def get_start_info(self):</li>
<li>def show_gameover_info(self):</li>
<li>def draw_score(self,score):</li>
<li>def terminate(self):<h3 id="ai"><a href="#ai" class="headerlink" title="ai"></a>ai</h3></li>
<li>这两天在网上看到一张让人涨姿势的图片，图片中展示的是贪吃蛇游戏， 估计大部分人都玩过。但如果仅仅是贪吃蛇游戏，那么它就没有什么让人涨姿势的地方了。 问题的关键在于，图片中的贪吃蛇真的很贪吃XD，它把矩形中出现的食物吃了个遍， 然后华丽丽地把整个矩形填满，真心是看得赏心悦目。作为一个CSer， 第一个想到的是，这东西是写程序实现的(因为，一般人干不出这事。 果断是要让程序来干的)第二个想到的是，写程序该如何实现，该用什么算法？ 既然开始想了，就开始做。因为Talk is cheap，要show me the code才行。 (从耗子叔那学来的)</li>
<li><p>最初的问题</p>
<ul>
<li>在一个矩形中，每一时刻有一个食物，贪吃蛇要在不撞到自己的条件下，<br>找到一条路(未必要最优)，然后沿着这条路运行，去享用它的美食</li>
</ul>
</li>
<li><p>简单版本</p>
<ul>
<li>我们先不去想蛇会越来越长这个事实，问题基本就是，给你一个起点(蛇头)和一个终点( 食物)，要</li>
<li>BFS</li>
<li>DFS</li>
<li>A*</li>
</ul>
</li>
<li><p>1.1版本</p>
<ul>
<li>但是当蛇身变长，很容易就遇到看不到食物困境，我们可以选择的方法有：<ul>
<li>让蛇随便走走</li>
</ul>
</li>
</ul>
</li>
<li>2.0版本<ul>
<li>思考以下这些问题<ul>
<li>蛇和食物间有路径直接就去吃，不可取。那该怎么办？</li>
<li>如果蛇去吃食物后，布局是安全的，是否就直接去吃？(这样最优吗？)</li>
<li>怎样定义布局是否安全？<ul>
<li>跟着蛇尾走。只要蛇和食物间不存在路径， 蛇就一直跟着蛇尾走</li>
</ul>
</li>
<li>蛇和食物之间如果没有路径，怎么办？</li>
<li>最短路径是否最优？(这个明显不是了)</li>
<li>那么，如果布局安全的情况下，最短路径是否最优？</li>
<li>除了最短路径，我们还可以怎么走？S形？最长？</li>
<li>怎么应对蛇身越来越长这个问题？</li>
<li>食物是随机出现的，有没可能出现无解的布局？</li>
<li>暴力法(brute force)能否得到最优序列？(让贪吃蛇尽可能地多吃食物)</li>
</ul>
</li>
<li>策略1：判断食物与尾巴之间是否存在路径<ul>
<li>不存在尾巴到达食物的路径<ul>
<li>自己看不见尾巴<ul>
<li>随机撞墙</li>
</ul>
</li>
<li>可以看见尾巴<ul>
<li>追尾</li>
</ul>
</li>
</ul>
</li>
<li>存在尾巴到达食物的路径（安全）<ul>
<li>看不见食物<ul>
<li>看不见尾巴<ul>
<li>随机</li>
</ul>
</li>
<li>看见尾巴<ul>
<li>追尾</li>
</ul>
</li>
</ul>
</li>
<li>看见食物<ul>
<li>吃食物</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>策略2：<ul>
<li>如果存在尾巴到达食物的安全路径，且可以看到食物<ul>
<li>吃食物</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/python/manim/manim%E5%9F%BA%E7%A1%80%E6%95%B4%E7%90%86/" data-id="cl403sxsn004wf8vu849jhmlx" data-title="manim基础整理" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-虚幻4引擎开发/第一课-什么是虚幻4" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/01/02/%E8%99%9A%E5%B9%BB4%E5%BC%95%E6%93%8E%E5%BC%80%E5%8F%91/%E7%AC%AC%E4%B8%80%E8%AF%BE-%E4%BB%80%E4%B9%88%E6%98%AF%E8%99%9A%E5%B9%BB4/" class="article-date">
  <time class="dt-published" datetime="2022-01-02T05:30:22.000Z" itemprop="datePublished">2022-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/notes-on-computer-expertise/categories/%E8%99%9A%E5%B9%BB4/">虚幻4</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/notes-on-computer-expertise/2022/01/02/%E8%99%9A%E5%B9%BB4%E5%BC%95%E6%93%8E%E5%BC%80%E5%8F%91/%E7%AC%AC%E4%B8%80%E8%AF%BE-%E4%BB%80%E4%B9%88%E6%98%AF%E8%99%9A%E5%B9%BB4/">第一课 什么是虚幻4</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="一、什么是虚幻4"><a href="#一、什么是虚幻4" class="headerlink" title="一、什么是虚幻4"></a>一、什么是虚幻4</h1><h1 id="二、虚幻4用途"><a href="#二、虚幻4用途" class="headerlink" title="二、虚幻4用途"></a>二、虚幻4用途</h1><ul>
<li>行业：<ul>
<li>建筑行业</li>
<li>室内表现</li>
<li>游戏</li>
<li>模拟仿真系统</li>
<li>购物</li>
</ul>
</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/%E8%99%9A%E5%B9%BB4%E5%BC%95%E6%93%8E%E5%BC%80%E5%8F%91/%E7%AC%AC%E4%B8%80%E8%AF%BE-%E4%BB%80%E4%B9%88%E6%98%AF%E8%99%9A%E5%B9%BB4/" data-id="cl403sxsi004ff8vu3qwobqa2" data-title="第一课 什么是虚幻4" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  


  <nav id="page-nav">
    
    <a class="extend prev" rel="prev" href="/notes-on-computer-expertise/page/5/">&laquo; Prev</a><a class="page-number" href="/notes-on-computer-expertise/">1</a><span class="space">&hellip;</span><a class="page-number" href="/notes-on-computer-expertise/page/4/">4</a><a class="page-number" href="/notes-on-computer-expertise/page/5/">5</a><span class="page-number current">6</span><a class="page-number" href="/notes-on-computer-expertise/page/7/">7</a><a class="page-number" href="/notes-on-computer-expertise/page/8/">8</a><span class="space">&hellip;</span><a class="page-number" href="/notes-on-computer-expertise/page/14/">14</a><a class="extend next" rel="next" href="/notes-on-computer-expertise/page/7/">Next &raquo;</a>
  </nav>

</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Categories</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/C-%E5%B7%A5%E5%85%B7/">C#工具</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/C-%E5%B7%A5%E5%85%B7/">C++工具</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/Mysql/">Mysql</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/blender/">blender</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/linux/">linux</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/manim/">manim</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/spring/">spring</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C/">vba基础操作</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E5%88%9B%E9%80%A0%E6%A8%A1%E5%BC%8F/">创造模式</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/">操作系统</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/">操作系统基础知识</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%BC%80%E5%8F%91/">操作系统开发</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%95%B0%E5%AD%A6/">数学</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/">数据结构</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/">数据结构基础知识</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%A8%A1%E6%9D%BF/">模板</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/">深度学习</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%8B%B1%E8%AF%AD/">英语</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%99%9A%E5%B9%BB4/">虚幻4</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BB%84%E6%88%90%E5%8E%9F%E7%90%86/">计算机组成原理</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/">计算机网络</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tags</h3>
    <div class="widget">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/notes-on-computer-expertise/tags/C/" rel="tag">C++</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tag Cloud</h3>
    <div class="widget tagcloud">
      <a href="/notes-on-computer-expertise/tags/C/" style="font-size: 10px;">C++</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Archives</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/05/">May 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/03/">March 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/02/">February 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/01/">January 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2021/12/">December 2021</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Recent Posts</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/31/vue/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6%E9%AA%8C%E8%AF%81%E7%A0%81%E6%97%A0%E6%B3%95%E6%98%BE%E7%A4%BA/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/26/%E6%95%B0%E6%8D%AE%E5%BA%93/navicat%E5%AE%89%E8%A3%85/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/25/%E8%99%9A%E5%B9%BB4%E5%BC%95%E6%93%8E%E5%BC%80%E5%8F%91/%E8%99%9A%E5%B9%BB%E5%9B%9B%20mod%E5%88%B6%E4%BD%9C/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/23/python/python%E6%89%B9%E9%87%8F%E7%94%9F%E6%88%90%E6%95%B0%E6%8D%AE/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/23/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C%E7%AC%94%E8%AE%B0/EXCEL%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/">(no title)</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      
      &copy; 2022 John Doe<br>
      Powered by <a href="https://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>

    </div>
    <nav id="mobile-nav">
  
    <a href="/notes-on-computer-expertise/" class="mobile-nav-link">Home</a>
  
    <a href="/notes-on-computer-expertise/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    


<script src="/notes-on-computer-expertise/js/jquery-3.4.1.min.js"></script>



  
<script src="/notes-on-computer-expertise/fancybox/jquery.fancybox.min.js"></script>




<script src="/notes-on-computer-expertise/js/script.js"></script>





  </div>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
        tex2jax: {
            inlineMath: [ ["$","$"], ["\\(","\\)"] ],
            skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'],
            processEscapes: true
        }
    });
    MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax();
        for (var i = 0; i < all.length; ++i)
            all[i].SourceElement().parentNode.className += ' has-jax';
    });
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML"></script>
</body>
</html>